diff --git a/docker-compose.yml b/docker-compose.yml index bf0627510..84262f28a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,7 +25,7 @@ services: volumes: - ./docker/mariadb:/var/lib/mysql - profiles: [mariadb] + profiles: [amavis] rq: build: @@ -115,6 +115,19 @@ services: depends_on: - api + amavis: + container_name: modoboa_amavis + restart: unless-stopped + build: + context: . + dockerfile: docker/Dockerfile.amavis.dev + profiles: [amavis] + ports: + - 9998:9998 + volumes: + - ./docker/amavis_config/amavisd.conf:/etc/amavisd.conf + command: amavisd debug + dovecot: container_name: modoboa_dovecot restart: unless-stopped diff --git a/frontend/src/api/amavis.js b/frontend/src/api/amavis.js index 327f903dc..4091cc559 100644 --- a/frontend/src/api/amavis.js +++ b/frontend/src/api/amavis.js @@ -7,16 +7,36 @@ export default { releaseSelection(data) { return repository.post('/amavis/quarantine/release_selection/', data) }, + releaseMessage(mailid, data) { + return repository.post(`/amavis/quarantine/${mailid}/release/`, data) + }, deleteSelection(data) { return repository.post('/amavis/quarantine/delete_selection/', data) }, + deleteMessage(mailid, data) { + return repository.post(`/amavis/quarantine/${mailid}/delete/`, data) + }, markMessageSelection(data) { return repository.post('/amavis/quarantine/mark_selection/', data) }, - getMessageContent(mailid, rcpt) { - return repository.get(`/amavis/quarantine/${mailid}/?rcpt=${rcpt}`) + getMessageContent(mailid, rcpt, secretId) { + const params = { rcpt } + if (secretId) { + params.secret_id = secretId + } + return repository.get(`/amavis/quarantine/${mailid}/`, { params }) }, - getMessageHeaders(mailid) { - return repository.get(`/amavis/quarantine/${mailid}/headers/`) + getMessageHeaders(mailid, rcpt, secretId) { + const params = { rcpt } + if (secretId) { + params.secret_id = secretId + } + return repository.get(`/amavis/quarantine/${mailid}/headers/`, { params }) + }, + getDomainPolicy(domainId) { + return repository.get(`/amavis/policies/${domainId}/`) + }, + updateDomainPolicy(domainId, data) { + return repository.patch(`/amavis/policies/${domainId}/`, data) }, } diff --git a/frontend/src/api/notifications.js b/frontend/src/api/notifications.js new file mode 100644 index 000000000..063a79cb4 --- /dev/null +++ b/frontend/src/api/notifications.js @@ -0,0 +1,7 @@ +import repository from './repository' + +export default { + getNotifications() { + return repository.get('/notifications/') + }, +} diff --git a/frontend/src/components/admin/domains/DomainEditForm.vue b/frontend/src/components/admin/domains/DomainEditForm.vue index 4f8bffc77..ed9dee532 100644 --- a/frontend/src/components/admin/domains/DomainEditForm.vue +++ b/frontend/src/components/admin/domains/DomainEditForm.vue @@ -172,7 +172,7 @@ - diff --git a/frontend/src/components/admin/domains/DomainPolicyForm.vue b/frontend/src/components/admin/domains/DomainPolicyForm.vue new file mode 100644 index 000000000..94559fb33 --- /dev/null +++ b/frontend/src/components/admin/domains/DomainPolicyForm.vue @@ -0,0 +1,168 @@ + + + diff --git a/frontend/src/components/admin/domains/form_steps/DomainPolicyForm.vue b/frontend/src/components/admin/domains/form_steps/DomainPolicyForm.vue new file mode 100644 index 000000000..31f8a8720 --- /dev/null +++ b/frontend/src/components/admin/domains/form_steps/DomainPolicyForm.vue @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/components/quarantine/QuarantineList.vue b/frontend/src/components/quarantine/QuarantineList.vue index 625455afe..4a111dccd 100644 --- a/frontend/src/components/quarantine/QuarantineList.vue +++ b/frontend/src/components/quarantine/QuarantineList.vue @@ -87,6 +87,7 @@ > + + + + + + + + + + +

+
+
+ + + + + + + + + + + + + +
This is an automated email, please do not reply
+

Dear Client

+

We've noticed that some of your account information appears to be missing or incorrect +We need to verify your account information in order to continue using your Apple ID, Please Verify your account information by clicking on the link below

Click here to Verify your ID

+


Thanks for choosing Apple,
Apple Team
+

© 2017 Apple. All rights reserved.

+

Email ID: 163327

diff --git a/modoboa/amavis/tests/sample_messages/quarantined-output-plain_nolinks.txt b/modoboa/amavis/tests/sample_messages/quarantined-output-plain_nolinks.txt new file mode 100644 index 000000000..2506f75ad --- /dev/null +++ b/modoboa/amavis/tests/sample_messages/quarantined-output-plain_nolinks.txt @@ -0,0 +1,17 @@ +
This is an automated email, please do not reply  
+
+# Dear Client
+
+We've noticed that some of your account information appears to be missing or
+incorrect We need to verify your account information in order to continue
+using your Apple ID, Please Verify your account information by clicking on the
+link below  
+
+ **Click here to Verify your ID**  
+
+Thanks for choosing Apple,  
+Apple Team  
+
+© 2017 Apple. All rights reserved.
+
+Email ID: 163327
\ No newline at end of file diff --git a/modoboa/amavis/tests/spamc b/modoboa/amavis/tests/spamc new file mode 100755 index 000000000..ea48afe5d --- /dev/null +++ b/modoboa/amavis/tests/spamc @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 5 diff --git a/modoboa/amavis/tests/test_checks.py b/modoboa/amavis/tests/test_checks.py new file mode 100644 index 000000000..6546491db --- /dev/null +++ b/modoboa/amavis/tests/test_checks.py @@ -0,0 +1,25 @@ +from django.test import TestCase +from django.test.utils import override_settings + +from ..checks import settings_checks + + +class CheckSessionCookieSecureTest(TestCase): + + databases = "__all__" + + @override_settings(AMAVIS_DEFAULT_DATABASE_ENCODING="LATIN-1") + def test_amavis_database_encoding_incorrect(self): + """ + If AMAVIS_DEFAULT_DATABASE_ENCODING is incorrect provide one warning. + """ + self.assertEqual( + settings_checks.check_amavis_database_encoding(None), [settings_checks.W001] + ) + + @override_settings(AMAVIS_DEFAULT_DATABASE_ENCODING="UTF-8") + def test_amavis_database_encoding_correct(self): + """ + If AMAVIS_DEFAULT_DATABASE_ENCODING is correct, there's no warning. + """ + self.assertEqual(settings_checks.check_amavis_database_encoding(None), []) diff --git a/modoboa/amavis/tests/test_handlers.py b/modoboa/amavis/tests/test_handlers.py new file mode 100644 index 000000000..cef291c2c --- /dev/null +++ b/modoboa/amavis/tests/test_handlers.py @@ -0,0 +1,214 @@ +"""Amavis tests.""" + +import os + +from django.test import override_settings +from django.urls import reverse + +from modoboa.admin import factories as admin_factories, models as admin_models +from modoboa.core import models as core_models +from modoboa.lib.tests import ModoAPITestCase +from modoboa.transport import factories as tr_factories +from .. import factories, lib, models + + +class DomainTestCase(ModoAPITestCase): + """Check that database is populated.""" + + databases = "__all__" + + def setUp(self): + """Initiate test context.""" + self.admin = core_models.User.objects.get(username="admin") + + def test_create_domain(self): + """Test domain creation.""" + domain = admin_factories.DomainFactory(name="domain.test") + name = f"@{domain.name}" + policy = models.Policy.objects.get(policy_name=name) + user = models.Users.objects.filter(policy=policy).first() + self.assertIsNot(user, None) + self.assertEqual(user.email, name) + + # Create a domain alias + self.client.force_authenticate(self.admin) + url = reverse("v2:domain_alias-list") + data = {"name": "dalias.test", "target": domain.pk} + resp = self.client.post(url, data, format="json") + self.assertEqual(resp.status_code, 201) + name = "@dalias.test" + self.assertFalse(models.Policy.objects.filter(policy_name=name).exists()) + user = models.Users.objects.get(email=name) + self.assertEqual(user.policy, policy) + + # Delete domain alias + url = reverse("v2:domain_alias-detail", args=[resp.json()["pk"]]) + resp = self.client.delete(url) + self.assertEqual(resp.status_code, 204) + self.assertFalse(models.Users.objects.filter(email=name).exists()) + + def test_rename_domain(self): + """Test domain rename.""" + domain = admin_factories.DomainFactory(name="domain.test") + domain.name = "domain1.test" + domain.save() + name = f"@{domain.name}" + self.assertTrue(models.Users.objects.filter(email=name).exists()) + self.assertTrue(models.Policy.objects.filter(policy_name=name).exists()) + + # Now from form + self.client.force_authenticate(self.admin) + rdomain = admin_factories.DomainFactory(name="domain.relay", type="relaydomain") + rdomain.transport = tr_factories.TransportFactory( + pattern=rdomain.name, + service="relay", + _settings={ + "relay_target_host": "external.host.tld", + "relay_target_port": "25", + "relay_verify_recipients": False, + }, + ) + rdomain.save() + url = reverse("v2:domain-detail", args=[rdomain.pk]) + values = { + "name": "domain2.relay", + "quota": rdomain.quota, + "default_mailbox_quota": rdomain.default_mailbox_quota, + "type": "relaydomain", + "enabled": rdomain.enabled, + "transport": { + "service": rdomain.transport.service, + "settings": { + "relay_target_host": "127.0.0.1", + "relay_target_port": 25, + }, + }, + } + resp = self.client.put(url, values, format="json") + self.assertTrue(resp.status_code, 200) + + def test_delete_domain(self): + """Test domain removal.""" + domain = admin_factories.DomainFactory(name="domain.test") + domain.delete(None) + name = f"@{domain.name}" + self.assertFalse(models.Users.objects.filter(email=name).exists()) + self.assertFalse(models.Policy.objects.filter(policy_name=name).exists()) + + def test_update_domain_policy(self): + """Check domain policy edition.""" + self.client.force_authenticate(self.admin) + domain = admin_factories.DomainFactory(name="domain.test") + url = reverse("v2:amavis-policy-detail", args=[domain.pk]) + custom_title = "This is SPAM!" + data = {"bypass_virus_checks": "Y", "spam_subject_tag2": custom_title} + resp = self.client.patch(url, data, format="json") + self.assertEqual(resp.status_code, 200) + name = f"@{domain.name}" + policy = models.Policy.objects.get(policy_name=name) + self.assertEqual(policy.spam_subject_tag2, custom_title) + + +@override_settings(SA_LOOKUP_PATH=(os.path.dirname(__file__),)) +class ManualLearningTestCase(ModoAPITestCase): + """Check manual learning mode.""" + + databases = "__all__" + + @classmethod + def setUpTestData(cls): # NOQA:N802 + """Create test data.""" + super().setUpTestData() + admin_factories.populate_database() + + def test_alias_creation(self): + """Check alias creation.""" + self.set_global_parameter("user_level_learning", True) + + # Fake activation because we don't have test data yet for + # amavis... + lib.setup_manual_learning_for_mbox( + admin_models.Mailbox.objects.get(address="user", domain__name="test.com") + ) + lib.setup_manual_learning_for_mbox( + admin_models.Mailbox.objects.get(address="admin", domain__name="test.com") + ) + + url = reverse("v2:alias-list") + values = { + "address": "alias1000@test.com", + "recipients": ["admin@test.com"], + "enabled": True, + } + resp = self.client.post(url, values, format="json") + self.assertEqual(resp.status_code, 201) + policy = models.Policy.objects.get(policy_name=values["recipients"][0]) + user = models.Users.objects.get(email=values["address"]) + self.assertEqual(user.policy, policy) + + values = { + "address": "user@test.com", + "recipients": ["admin@test.com"], + "enabled": True, + } + resp = self.client.post(url, values, format="json") + self.assertEqual(resp.status_code, 201) + policy = models.Policy.objects.get(policy_name=values["recipients"][0]) + user = models.Users.objects.get(email=values["address"]) + self.assertEqual(user.policy, policy) + + def test_mailbox_rename(self): + """Check rename case.""" + self.set_global_parameter("user_level_learning", True) + + lib.setup_manual_learning_for_mbox( + admin_models.Mailbox.objects.get(address="user", domain__name="test.com") + ) + + user = core_models.User.objects.get(username="user@test.com") + url = reverse("v2:account-detail", args=[user.pk]) + values = { + "username": "user2@test.com", + "role": "SimpleUsers", + "mailbox": {"use_domain_quota": True}, + } + resp = self.client.put(url, values, format="json") + self.assertEqual(resp.status_code, 200) + self.assertTrue(models.Users.objects.filter(email=values["username"]).exists()) + + def test_learn_alias_spam_as_admin(self): + """Check learning spam for an alias address as admin user.""" + user = core_models.User.objects.get(username="admin") + recipient_db = "user" + rcpt = "alias@test.com" + sender = "spam@evil.corp" + content = factories.SPAM_BODY.format(rcpt=rcpt, sender=sender) + + saclient = lib.SpamassassinClient(user, recipient_db) + result = saclient.learn_spam(rcpt, content) + self.assertTrue(result) + + def test_delete_catchall_alias(self): + """Check that Users record is not deleted.""" + self.set_global_parameter("user_level_learning", True) + + # Fake activation because we don't have test data yet for + # amavis... + lib.setup_manual_learning_for_mbox( + admin_models.Mailbox.objects.get(address="admin", domain__name="test.com") + ) + + url = reverse("v2:alias-list") + values = { + "address": "@test.com", + "recipients": ["admin@test.com"], + "enabled": True, + } + resp = self.client.post(url, values, format="json") + self.assertTrue(resp.status_code, 201) + + alias = admin_models.Alias.objects.get(address="@test.com") + url = reverse("v2:alias-detail", args=[alias.pk]) + resp = self.client.delete(url) + self.assertEqual(resp.status_code, 204) + self.assertTrue(models.Users.objects.get(email="@test.com")) diff --git a/modoboa/amavis/tests/test_lib.py b/modoboa/amavis/tests/test_lib.py new file mode 100644 index 000000000..e8ac04f04 --- /dev/null +++ b/modoboa/amavis/tests/test_lib.py @@ -0,0 +1,90 @@ +from django.test import SimpleTestCase + +from modoboa.lib.tests import ModoTestCase +from modoboa.amavis.lib import cleanup_email_address, make_query_args + + +class MakeQueryArgsTests(ModoTestCase): + """Tests for modoboa_amavis.lib.make_query_args().""" + + def test_simple_email_address_001(self): + """Check case insensitive email address without recipient delimiter.""" + self.set_global_parameter("localpart_is_case_sensitive", False) + self.set_global_parameter("recipient_delimiter", "") + address = "User+Foo@sub.exAMPLE.COM" + expected_output = [ + "user+foo@sub.example.com", + ] + output = make_query_args(address) + self.assertEqual(output, expected_output) + + def test_simple_email_address_002(self): + """Check case sensitive email address with recipient delimiter.""" + self.set_global_parameter("localpart_is_case_sensitive", True) + self.set_global_parameter("recipient_delimiter", "+") + address = "User+Foo@sub.exAMPLE.COM" + expected_output = [ + "User+Foo@sub.exAMPLE.COM", + "User+Foo@sub.example.com", + "User@sub.example.com", + ] + output = make_query_args(address) + self.assertEqual(output, expected_output) + + def test_simple_email_address_003(self): + """Check email address with recipient delimiter wildcard.""" + self.set_global_parameter("localpart_is_case_sensitive", False) + self.set_global_parameter("recipient_delimiter", "+") + address = "User+Foo@sub.exAMPLE.COM" + expected_output = [ + "user+foo@sub.example.com", + "user+.*@sub.example.com", + "user@sub.example.com", + ] + output = make_query_args(address, exact_extension=False, wildcard=".*") + self.assertEqual(output, expected_output) + + def test_simple_email_address_004(self): + """Check domain search.""" + self.set_global_parameter("localpart_is_case_sensitive", False) + self.set_global_parameter("recipient_delimiter", "+") + address = "User+Foo@sub.exAMPLE.COM" + expected_output = [ + "user+foo@sub.example.com", + "user+.*@sub.example.com", + "user@sub.example.com", + "@sub.example.com", + "@.", + ] + output = make_query_args( + address, exact_extension=False, wildcard=".*", domain_search=True + ) + self.assertEqual(output, expected_output) + + def test_simple_email_address_idn(self): + """Check email address with international domain name.""" + self.set_global_parameter("localpart_is_case_sensitive", False) + self.set_global_parameter("recipient_delimiter", "") + address = "Pingüino@Pájaro.Niño.exAMPLE.COM" + expected_output = [ + "Pingüino@Pájaro.Niño.exAMPLE.COM", + "pingüino@xn--pjaro-xqa.xn--nio-8ma.example.com", + ] + output = make_query_args(address) + self.assertEqual(output, expected_output) + + +class FixUTF8EncodingTests(SimpleTestCase): + """Tests for modoboa_amavis.lib.cleanup_email_address().""" + + def test_value_with_newline(self): + value = '"John Smith" \n' + expected_output = "John Smith " + output = cleanup_email_address(value) + self.assertEqual(output, expected_output) + + def test_no_name(self): + value = "" + expected_output = "john.smith@example.com" + output = cleanup_email_address(value) + self.assertEqual(output, expected_output) diff --git a/modoboa/amavis/tests/test_management_commands.py b/modoboa/amavis/tests/test_management_commands.py new file mode 100644 index 000000000..b4c27fd7a --- /dev/null +++ b/modoboa/amavis/tests/test_management_commands.py @@ -0,0 +1,45 @@ +"""Management commands tests.""" + +from dateutil.relativedelta import relativedelta + +from django.core.management import call_command +from django.utils import timezone + +from modoboa.lib.tests import ModoTestCase +from .. import factories, models + + +class ManagementCommandTestCase(ModoTestCase): + """Management commands tests.""" + + databases = "__all__" + + def test_qcleanup(self): + """Test qcleanup command.""" + factories.create_spam("user@test.com", rs="D") + call_command("qcleanup") + self.assertEqual(models.Quarantine.objects.count(), 0) + self.assertEqual(models.Msgs.objects.count(), 0) + self.assertEqual(models.Maddr.objects.count(), 0) + self.assertEqual(models.Msgrcpt.objects.count(), 0) + + factories.create_spam("user@test.com", rs="D") + msgrcpt = factories.create_spam("user@test.com", rs="R") + call_command("qcleanup") + # Should not raise anything + msgrcpt.refresh_from_db() + + self.set_global_parameter("released_msgs_cleanup", True) + call_command("qcleanup") + with self.assertRaises(models.Msgrcpt.DoesNotExist): + msgrcpt.refresh_from_db() + + msgrcpt = factories.create_spam("user@test.com") + msgrcpt.mail.time_num = int( + (timezone.now() - relativedelta(days=40)).strftime("%s") + ) + msgrcpt.mail.save(update_fields=["time_num"]) + self.set_global_parameter("max_messages_age", 30) + call_command("qcleanup") + with self.assertRaises(models.Msgrcpt.DoesNotExist): + msgrcpt.refresh_from_db() diff --git a/modoboa/amavis/tests/test_sql_email.py b/modoboa/amavis/tests/test_sql_email.py new file mode 100644 index 000000000..aac155f7e --- /dev/null +++ b/modoboa/amavis/tests/test_sql_email.py @@ -0,0 +1,67 @@ +"""Tests for sql_email.""" + +import os + +from django.test import TestCase +from django.utils.encoding import smart_str + +from ..sql_email import SQLemail + +SAMPLES_DIR = os.path.realpath( + os.path.join(os.path.dirname(__file__), "sample_messages") +) + + +class EmailTestImplementation(SQLemail): + + def _fetch_message(self): + message_path = os.path.join(SAMPLES_DIR, f"{self.mailid}-input.txt") + assert os.path.isfile(message_path), f"{message_path} does not exist." + + with open(message_path, "rb") as fp: + mail_text = fp.read() + + return mail_text + + +class EmailTests(TestCase): + """Tests for modoboa_amavis.sql_email.SQLEmail + + When writing new sample messages use the following naming convention for + the sample files stored in sample_messages: + + input: {message_id}-input.txt + output: {message_id}-output-{dformat}_{no,}links.txt + """ + + def _get_expected_output(self, message_id, **kwargs): + ext = kwargs["dformat"] if "dformat" in kwargs else "plain" + ext += "_links" if "links" in kwargs and kwargs["links"] else "_nolinks" + message_path = os.path.join(SAMPLES_DIR, f"{message_id}-output-{ext}.txt") + assert os.path.isfile(message_path), f"{message_path} does not exist." + + with open(message_path, "rb") as fp: + # output should always be unicode (py2) or str (py3) + mail_text = smart_str(fp.read()) + + return mail_text + + def _test_email(self, message_id, **kwargs): + """Boiler plate code for testing e-mails.""" + expected_output = self._get_expected_output(message_id, **kwargs) + output = EmailTestImplementation(message_id, **kwargs).body + self.assertEqual(output, expected_output) + + def test_amavis_aleart_header(self): + email = EmailTestImplementation("quarantined") + self.assertEqual(email.qtype, "BAD HEADER SECTION") + self.assertEqual( + email.qreason, + "Non-encoded non-ASCII data (and not UTF-8) (char 85 " + "hex): Subject: I think I saw you in my dreams\\x{85}", + ) + + def test_email_multipart_with_no_text(self): + """for a multipart message without a text/plain part convert the + text/html to text/plain""" + self._test_email("quarantined") diff --git a/modoboa/amavis/tests/test_utils.py b/modoboa/amavis/tests/test_utils.py new file mode 100644 index 000000000..06e063a6f --- /dev/null +++ b/modoboa/amavis/tests/test_utils.py @@ -0,0 +1,19 @@ +from django.test import SimpleTestCase + +from modoboa.amavis.utils import fix_utf8_encoding + + +class FixUTF8EncodingTests(SimpleTestCase): + """Tests for modoboa_amavis.utils.fix_utf8_encoding().""" + + def test_4_byte_unicode(self): + value = "\xf0\x9f\x99\x88" + expected_output = "\U0001f648" # == See No Evil Moneky + output = fix_utf8_encoding(value) + self.assertEqual(output, expected_output) + + def test_truncated_4_byte_unicode(self): + value = "\xf0\x9f\x99" + expected_output = "\xf0\x9f\x99" + output = fix_utf8_encoding(value) + self.assertEqual(output, expected_output) diff --git a/modoboa/amavis/tests/test_viewsets.py b/modoboa/amavis/tests/test_viewsets.py new file mode 100644 index 000000000..0bf96a822 --- /dev/null +++ b/modoboa/amavis/tests/test_viewsets.py @@ -0,0 +1,288 @@ +import os +from unittest import mock + +from rq import SimpleWorker + +from django.core import mail +from django.core.management import call_command +from django.test import override_settings +from django.urls import reverse + +import django_rq + +from modoboa.admin import factories as admin_factories, models as admin_models +from modoboa.amavis import factories, models +from modoboa.amavis.utils import smart_str +from modoboa.core import models as core_models +from modoboa.lib.tests import ModoAPITestCase + + +class TestDataMixin: + """A mixin to provide test data.""" + + @classmethod + def setUpTestData(cls): # NOQA:N802 + """Create some content.""" + super().setUpTestData() + cls.msgrcpt = factories.create_spam("user@test.com") + + +@override_settings(SA_LOOKUP_PATH=(os.path.dirname(__file__),)) +class QuarantineViewSetTestCase(TestDataMixin, ModoAPITestCase): + + databases = "__all__" + + @classmethod + def setUpTestData(cls): # NOQA:N802 + """Create test data.""" + super().setUpTestData() + admin_factories.populate_database() + + def tearDown(self): + """Restore msgrcpt state.""" + self.msgrcpt.rs = " " + self.msgrcpt.save(update_fields=["rs"]) + self.set_global_parameter("domain_level_learning", False) + self.set_global_parameter("user_level_learning", False) + + def test_list(self): + url = reverse("v2:amavis-quarantine-list") + resp = self.client.get(url) + self.assertEqual(resp.status_code, 200) + content = resp.json() + self.assertEqual(content["count"], 1) + + resp = self.client.get(f"{url}?search=pouet&criteria=both") + content = resp.json() + self.assertEqual(content["count"], 0) + + resp = self.client.get(f"{url}?msgtype=V") + content = resp.json() + self.assertEqual(content["count"], 0) + factories.create_virus("user@test.com") + resp = self.client.get(f"{url}?msgtype=V") + content = resp.json() + self.assertEqual(content["count"], 1) + + def test_retrieve(self): + mail_id = smart_str(self.msgrcpt.mail.mail_id) + rcpt = smart_str(self.msgrcpt.rid.email) + url = reverse("v2:amavis-quarantine-detail", args=[mail_id]) + resp = self.client.get(f"{url}?rcpt={rcpt}") + self.assertEqual(resp.status_code, 200) + + def test_headers(self): + mail_id = smart_str(self.msgrcpt.mail.mail_id) + url = reverse("v2:amavis-quarantine-headers", args=[mail_id]) + resp = self.client.get(url) + self.assertEqual(resp.status_code, 200) + + def test_headers_selfservice(self): + self.client.logout() + mail_id = smart_str(self.msgrcpt.mail.mail_id) + url = reverse("v2:amavis-quarantine-headers", args=[mail_id]) + url = f"{url}?secret_id={smart_str(self.msgrcpt.mail.secret_id)}" + response = self.client.get(url) + self.assertEqual(response.status_code, 403) + + self.set_global_parameter("self_service", True) + response = self.client.get(url) + self.assertEqual(response.status_code, 403) + url = f"{url}&rcpt={smart_str(self.msgrcpt.rid.email)}" + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + def test_delete(self): + mail_id = smart_str(self.msgrcpt.mail.mail_id) + url = reverse("v2:amavis-quarantine-delete", args=[mail_id]) + data = {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)} + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, 204) + self.msgrcpt.refresh_from_db() + self.assertEqual(self.msgrcpt.rs, "D") + + def test_delete_selfservice(self): + self.client.logout() + mail_id = smart_str(self.msgrcpt.mail.mail_id) + url = reverse("v2:amavis-quarantine-delete", args=[mail_id]) + data = {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)} + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, 403) + self.set_global_parameter("self_service", True) + data["secret_id"] = smart_str(self.msgrcpt.mail.secret_id) + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, 204) + self.msgrcpt.refresh_from_db() + self.assertEqual(self.msgrcpt.rs, "D") + + def test_delete_selection(self): + mail_id = smart_str(self.msgrcpt.mail.mail_id) + url = reverse("v2:amavis-quarantine-delete-selection") + data = { + "selection": [ + {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)} + ] + } + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, 204) + self.msgrcpt.refresh_from_db() + self.assertEqual(self.msgrcpt.rs, "D") + + @mock.patch("socket.socket") + def test_release(self, mock_socket): + mock_socket.return_value.recv.return_value = b"250 1234 Ok\r\n" + mail_id = smart_str(self.msgrcpt.mail.mail_id) + url = reverse("v2:amavis-quarantine-release", args=[mail_id]) + data = {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)} + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, 200) + self.msgrcpt.refresh_from_db() + self.assertEqual(self.msgrcpt.rs, "R") + + @mock.patch("socket.socket") + def test_release_selfservice(self, mock_socket): + """Test release view.""" + mock_socket.return_value.recv.return_value = b"250 1234 Ok\r\n" + self.client.logout() + mail_id = smart_str(self.msgrcpt.mail.mail_id) + url = reverse("v2:amavis-quarantine-release", args=[mail_id]) + self.set_global_parameter("self_service", True) + # Missing rcpt -> fails + data = {"mailid": mail_id, "secret_id": "1234"} + resp = self.client.post(url, data, format="json") + self.assertEqual(resp.status_code, 403) + # Wrong secret_id -> fails + data["rcpt"] = smart_str(self.msgrcpt.rid.email) + resp = self.client.post(url, data, format="json") + self.assertEqual(resp.status_code, 403) + # Request mode + data["secret_id"] = smart_str(self.msgrcpt.mail.secret_id) + resp = self.client.post(url, data, format="json") + self.assertEqual(resp.status_code, 200) + self.msgrcpt.refresh_from_db() + self.assertEqual(self.msgrcpt.rs, "p") + # Direct release mode + self.set_global_parameter("user_can_release", True) + resp = self.client.post(url, data, format="json") + self.assertEqual(resp.status_code, 200) + self.msgrcpt.refresh_from_db() + self.assertEqual(self.msgrcpt.rs, "R") + + def test_release_request(self): + """Test release request mode.""" + user = core_models.User.objects.get(username="user@test.com") + self.client.force_authenticate(user) + + mail_id = smart_str(self.msgrcpt.mail.mail_id) + url = reverse("v2:amavis-quarantine-release", args=[mail_id]) + data = {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)} + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, 200) + self.msgrcpt.refresh_from_db() + self.assertEqual(self.msgrcpt.rs, "p") + + # Send notification to admins + call_command("amnotify") + self.assertEqual(len(mail.outbox), 1) + + def _test_mark_selection(self, action, status): + """Mark message common code.""" + mail_id = smart_str(self.msgrcpt.mail.mail_id) + url = reverse("v2:amavis-quarantine-mark-selection") + data = { + "selection": [ + {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)} + ], + "type": action, + } + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, 204) + queue = django_rq.get_queue("modoboa") + worker = SimpleWorker([queue], connection=queue.connection) + worker.work(burst=True) + self.msgrcpt.refresh_from_db() + self.assertEqual(self.msgrcpt.rs, status) + + self.msgrcpt.rs = " " + self.msgrcpt.save(update_fields=["rs"]) + self.set_global_parameter("sa_is_local", False) + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, 204) + worker.work(burst=True) + self.msgrcpt.refresh_from_db() + self.assertEqual(self.msgrcpt.rs, status) + + def test_mark_as_ham(self): + """Test mark_as_ham view.""" + self._test_mark_selection("ham", "H") + + # Check domain level learning + self.set_global_parameter("domain_level_learning", True) + data = { + "selection": [ + { + "rcpt": smart_str(self.msgrcpt.rid.email), + "mailid": smart_str(self.msgrcpt.mail.mail_id), + }, + ], + "type": "ham", + "database": "domain", + } + url = reverse("v2:amavis-quarantine-mark-selection") + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, 204) + + # Check user level learning + self.set_global_parameter("user_level_learning", True) + data["database"] = "user" + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, 204) + + def test_mark_as_spam(self): + """Test mark_as_spam view.""" + self._test_mark_selection("spam", "S") + + def test_manual_learning_as_user(self): + """Test learning when connected as a simple user.""" + user = core_models.User.objects.get(username="user@test.com") + self.client.force_authenticate(user) + mail_id = smart_str(self.msgrcpt.mail.mail_id) + data = { + "selection": [ + {"mailid": mail_id, "rcpt": smart_str(self.msgrcpt.rid.email)} + ], + "type": "ham", + } + url = reverse("v2:amavis-quarantine-mark-selection") + response = self.client.post(url, data, format="json") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), {"status": "ok"}) + self.set_global_parameter("user_level_learning", True) + self._test_mark_selection("ham", "H") + + +class PolicyViewSetTestCase(ModoAPITestCase): + + databases = "__all__" + + @classmethod + def setUpTestData(cls): # NOQA:N802 + """Create test data.""" + super().setUpTestData() + admin_factories.populate_database() + + def test_get_policy(self): + domain = admin_models.Domain.objects.get(name="test.com") + url = reverse("v2:amavis-policy-detail", args=[domain.pk]) + resp = self.client.get(url) + self.assertEqual(resp.status_code, 200) + + def test_update_policy(self): + domain = admin_models.Domain.objects.get(name="test.com") + data = {"bypass_virus_checks": "Y", "spam_tag2_level": 6} + url = reverse("v2:amavis-policy-detail", args=[domain.pk]) + resp = self.client.patch(url, data, format="json") + self.assertEqual(resp.status_code, 200) + p = models.Policy.objects.get(users__email="@test.com") + self.assertEqual(p.bypass_virus_checks, data["bypass_virus_checks"]) + self.assertEqual(p.spam_tag2_level, data["spam_tag2_level"]) diff --git a/modoboa/amavis/urls.py b/modoboa/amavis/urls.py new file mode 100644 index 000000000..ad3d4e1a9 --- /dev/null +++ b/modoboa/amavis/urls.py @@ -0,0 +1,11 @@ +"""Amavis urls.""" + +from rest_framework import routers + +from modoboa.amavis import viewsets + +router = routers.SimpleRouter() +router.register(r"quarantine", viewsets.QuarantineViewSet, basename="amavis-quarantine") +router.register(r"policies", viewsets.PolicyViewSet, basename="amavis-policy") + +urlpatterns = router.urls diff --git a/modoboa/amavis/utils.py b/modoboa/amavis/utils.py new file mode 100644 index 000000000..d829eb02d --- /dev/null +++ b/modoboa/amavis/utils.py @@ -0,0 +1,105 @@ +"""A collection of utility functions for working with the Amavis database.""" + +import chardet + +from django.conf import settings +from django.db.models.expressions import Func +from django.utils.encoding import ( + smart_bytes as django_smart_bytes, + smart_str as django_smart_str, +) + + +""" +Byte fields for text data are EVIL. + +MySQL uses `varbyte` fields which mysqlclient client maps to `str` (Py2) or +`bytes` (Py3), Djangos smart_* functions work as expected. + +PostgreSQL uses `bytea` fields which psycopg2 maps to `memoryview`, +Djangos smart_* functions don't work as expected, you must call `tobytes()` on +the memoryview for them to work. + +For convenience use smart_bytes and smart_str from this file in modoboa.amavis +to avoid any headaches. +""" + + +def smart_bytes(value, *args, **kwargs): + if isinstance(value, memoryview): + value = value.tobytes() + return django_smart_bytes(value, *args, **kwargs) + + +def smart_str(value, *args, **kwargs): + if isinstance(value, memoryview): + value = value.tobytes() + return django_smart_str(value, *args, **kwargs) + + +def fix_utf8_encoding(value): + """Fix utf-8 strings that contain utf-8 escaped characters. + + msgs.from_addr and msgs.subject potentialy contain badly escaped utf-8 + characters, this utility function fixes that and should be used anytime + these fields are accesses. + + Didn't even know the raw_unicode_escape encoding existed :) + https://docs.python.org/3/library/codecs.html?highlight=raw_unicode_escape#python-specific-encodings + """ + assert isinstance(value, str), "value should be of type str" + + if len(value) == 0: + # short circuit for empty strings + return "" + + bytes_value = value.encode("raw_unicode_escape") + try: + value = bytes_value.decode("utf-8") + except UnicodeDecodeError: + encoding = chardet.detect(bytes_value) + try: + value = bytes_value.decode(encoding["encoding"], "replace") + except (TypeError, UnicodeDecodeError): + # ??? use the original value, we've done our best to try and + # convert it to a clean utf-8 string. + pass + + return value + + +class ConvertFrom(Func): + """Convert a binary value to a string. + Calls the database specific function to convert a binary value to a string + using the encoding set in AMAVIS_DEFAULT_DATABASE_ENCODING. + """ + + """PostgreSQL implementation. + See https://www.postgresql.org/docs/9.3/static/functions-string.html#FUNCTIONS-STRING-OTHER""" # NOQA:E501 + function = "convert_from" + arity = 1 + template = ( + f"%(function)s(%(expressions)s, '{settings.AMAVIS_DEFAULT_DATABASE_ENCODING}')" + ) + + def as_mysql(self, compiler, connection): + """MySQL implementation. + See https://dev.mysql.com/doc/refman/5.5/en/cast-functions.html#function_convert + """ # NOQA:E501 + return super().as_sql( + compiler, + connection, + function="CONVERT", + template=f"%(function)s(%(expressions)s USING {settings.AMAVIS_DEFAULT_DATABASE_ENCODING})", + arity=1, + ) + + def as_sqlite(self, compiler, connection): + """SQLite implementation. + SQLite has no equivilant function, just return the field.""" + return super().as_sql( + compiler, + connection, + template="%(expressions)s", + arity=1, + ) diff --git a/modoboa/amavis/viewsets.py b/modoboa/amavis/viewsets.py new file mode 100644 index 000000000..0033e75de --- /dev/null +++ b/modoboa/amavis/viewsets.py @@ -0,0 +1,265 @@ +"""Amavis viewsets.""" + +from django.http import Http404 +from django.shortcuts import get_object_or_404 +from django.utils.translation import gettext as _ + +import django_rq +from rest_framework import filters, mixins, permissions, response, viewsets +from rest_framework.decorators import action + +from modoboa.amavis.sql_connector import SQLconnector +from modoboa.lib.paginator import Paginator +from modoboa.lib.permissions import CanViewDomain + +from modoboa.admin import models as admin_models +from modoboa.amavis import models, serializers, tasks +from modoboa.amavis.lib import ( + AMrelease, + manual_learning_enabled, + SelfServiceAuthentication, +) +from modoboa.amavis.sql_email import SQLemail +from modoboa.amavis.utils import smart_str +from modoboa.parameters import tools as param_tools + + +SELFSERVICE_ACTIONS = ["retrieve", "headers", "delete", "release"] + + +def get_user_valid_addresses(user): + """Retrieve all valid addresses of a user.""" + valid_addresses = [] + if user.role == "SimpleUsers": + valid_addresses.append(user.email) + try: + mb = admin_models.Mailbox.objects.get(user=user) + except admin_models.Mailbox.DoesNotExist: + pass + else: + valid_addresses += mb.alias_addresses + return valid_addresses + + +class QuarantineViewSet(viewsets.GenericViewSet): + + filter_backends = (filters.OrderingFilter,) + ordering_fields = [ + "datetime", + "from_address", + "score", + "subject", + "to_address", + "type", + ] + permission_classes = (permissions.IsAuthenticated,) + + def get_serializer_class(self): + if self.action == "retrieve": + return serializers.MessageSerializer + if self.action == "headers": + return serializers.MessageHeadersSerializer + if self.action == "mark_selection": + return serializers.MarkMessageSelectionSerializer + if self.action in ["release_selection", "delete_selection"]: + return serializers.MessageSelectionSerializer + if self.action in ["delete", "release"]: + return serializers.MessageIdentifierSerializer + return serializers.PaginatedMessageListSerializer + + def get_authenticators(self): + result = [auth() for auth in self.authentication_classes] + if self.request: + action = self.action_map.get(self.request.method.lower()) + if action in SELFSERVICE_ACTIONS: + result = [SelfServiceAuthentication()] + result + return result + + def get_permissions(self): + if self.request.auth == "selfservice" and self.action in SELFSERVICE_ACTIONS: + return [] + return super().get_permissions() + + def list(self, request): + ordering = request.GET.get("ordering") + connector = SQLconnector(user=request.user, ordering=ordering) + try: + page_size = int(request.GET.get("page_size")) + except (TypeError, ValueError): + page_size = request.user.parameters.get_value("messages_per_page") + total = connector.messages_count(request) + paginator = Paginator(total, page_size) + page_num = int(request.GET.get("page", 1)) + page = paginator.getpage(page_num) + if not page: + serializer = self.get_serializer( + { + "count": 0, + "first_index": 0, + "last_index": 0, + "prev_page": None, + "next_page": None, + "results": [], + } + ) + return response.Response(serializer.data) + email_list = connector.fetch(page.id_start, page.id_stop) + serializer = self.get_serializer( + { + "count": total, + "first_index": page_num * page_size, + "last_index": (page_num * page_size) + len(email_list), + "prev_page": page.previous_page_number if page.has_previous else None, + "next_page": page.next_page_number if page.has_next else None, + "results": email_list, + } + ) + return response.Response(serializer.data) + + def retrieve(self, request, pk): + rcpt = request.GET.get("rcpt") + if rcpt is None: + return response.Response({"error": _("Invalid request")}, status=400) + if request.user: + if request.user.email == rcpt: + SQLconnector().set_msgrcpt_status(rcpt, pk, "V") + elif hasattr(request.user, "mailbox"): + mb = request.user.mailbox + if rcpt == mb.full_address or rcpt in mb.alias_addresses: + SQLconnector().set_msgrcpt_status(rcpt, pk, "V") + mail = SQLemail(pk.encode("ascii"), dformat="plain") + serializer = self.get_serializer(mail) + return response.Response(serializer.data) + + @action(methods=["get"], detail=True) + def headers(self, request, pk): + email = SQLemail(pk.encode("ascii")) + headers = [] + for name in email.msg.keys(): + headers.append({"name": name, "value": email.get_header(email.msg, name)}) + serializer = self.get_serializer({"headers": headers}) + return response.Response(serializer.data) + + def _release_selection(self, request, selection): + connector = SQLconnector() + valid_addresses = None + if request.user: + valid_addresses = get_user_valid_addresses(request.user) + msgrcpts = [] + for item in selection: + if valid_addresses and item["rcpt"] not in valid_addresses: + continue + msgrcpts += [ + ( + item["mailid"], + connector.get_recipient_message(item["rcpt"], item["mailid"]), + ) + ] + if ( + not request.user or request.user.role == "SimpleUsers" + ) and not param_tools.get_global_parameter("user_can_release"): + for i, msgrcpt in msgrcpts: + connector.set_msgrcpt_status(smart_str(msgrcpt.rid.email), i, "p") + return response.Response({"status": "pending"}) + + amr = AMrelease() + error = None + for mid, rcpt in msgrcpts: + # we can't use the .mail relation on rcpt because it leads to + # an error on Postgres (memoryview pickle error). + mail = models.Msgs.objects.get(pk=mid.encode("ascii")) + result = amr.sendreq(mid, mail.secret_id, rcpt.rid.email) + if result: + connector.set_msgrcpt_status(smart_str(rcpt.rid.email), mid, "R") + else: + error = result + break + + if error: + return response.Response({"status": error}) + + return response.Response({"status": "released"}) + + @action(methods=["post"], detail=False) + def release_selection(self, request): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + return self._release_selection(request, serializer.validated_data["selection"]) + + @action(methods=["post"], detail=True) + def release(self, request, pk): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + return self._release_selection(request, [serializer.validated_data]) + + def _delete_selection(self, request, selection): + connector = SQLconnector() + valid_addresses = None + if request.user: + valid_addresses = get_user_valid_addresses(request.user) + for item in selection: + if valid_addresses and item["rcpt"] not in valid_addresses: + continue + connector.set_msgrcpt_status(item["rcpt"], item["mailid"], "D") + return response.Response(status=204) + + @action(methods=["post"], detail=False) + def delete_selection(self, request): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + return self._delete_selection(request, serializer.validated_data["selection"]) + + @action(methods=["post"], detail=True) + def delete(self, request, pk): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + return self._delete_selection(request, [serializer.validated_data]) + + @action(methods=["post"], detail=False) + def mark_selection(self, request): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + if not manual_learning_enabled(request.user): + return response.Response({"status": "ok"}) + recipient_db = serializer.validated_data.get("database") + if not recipient_db: + recipient_db = "user" if request.user.role == "SimpleUsers" else "global" + queue = django_rq.get_queue("modoboa") + queue.enqueue( + tasks.manual_learning, + request.user.pk, + serializer.validated_data["type"], + serializer.validated_data["selection"], + recipient_db, + ) + return response.Response(status=204) + + +class PolicyViewSet( + mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet +): + + permission_classes = (permissions.IsAuthenticated, CanViewDomain) + serializer_class = serializers.PolicySerializer + + def get_queryset(self): + domains = [ + f"@{name}" + for name in admin_models.Domain.objects.get_for_admin( + self.request.user + ).values_list("name", flat=True) + ] + return models.Policy.objects.filter(users__email__in=domains) + + def get_object(self): + """Return the object the view is displaying.""" + domain = get_object_or_404(admin_models.Domain, pk=self.kwargs["pk"]) + queryset = self.filter_queryset(self.get_queryset()) + obj = queryset.filter(users__email=f"@{domain.name}").first() + if obj is None: + raise Http404 + + # May raise a permission denied + self.check_object_permissions(self.request, obj) + + return obj diff --git a/modoboa/core/api/v2/serializers.py b/modoboa/core/api/v2/serializers.py index 302cf81a2..791587143 100644 --- a/modoboa/core/api/v2/serializers.py +++ b/modoboa/core/api/v2/serializers.py @@ -664,9 +664,11 @@ class NotificationSerializer(serializers.Serializer): """Serializer used to render a notification.""" id = serializers.CharField() - url = serializers.CharField(required=False) text = serializers.CharField() - level = serializers.CharField() + color = serializers.CharField() + target = serializers.CharField() + url = serializers.CharField(required=False) + counter = serializers.IntegerField(required=False) class ModoboaApplicationSerializer(serializers.Serializer): diff --git a/modoboa/core/api/v2/urls.py b/modoboa/core/api/v2/urls.py index f81b88be8..b6ca5ab78 100644 --- a/modoboa/core/api/v2/urls.py +++ b/modoboa/core/api/v2/urls.py @@ -32,11 +32,6 @@ urlpatterns += [ views.ComponentsInformationAPIView.as_view(), name="components_information", ), - path( - "admin/notifications/", - views.NotificationsAPIView.as_view(), - name="notifications", - ), path( "admin/news_feed/", views.NewsFeedAPIView.as_view(), @@ -48,5 +43,10 @@ urlpatterns += [ name="statistics", ), path("capabilities/", views.CapabilitiesAPIView.as_view(), name="capabilities"), + path( + "notifications/", + views.NotificationsAPIView.as_view(), + name="notifications", + ), path("theme/", views.ThemeAPIView.as_view(), name="theme"), ] diff --git a/modoboa/core/api/v2/views.py b/modoboa/core/api/v2/views.py index c8b97e29c..3f6685d24 100644 --- a/modoboa/core/api/v2/views.py +++ b/modoboa/core/api/v2/views.py @@ -142,7 +142,9 @@ class PasswordResetConfirmView(APIView): class ComponentsInformationAPIView(APIView): """Retrieve information about installed components.""" - permission_classes = [permissions.IsAuthenticated, IsSuperUser] + permission_classes = [ + permissions.IsAuthenticated, + ] throttle_classes = [UserLesserDdosUser] @extend_schema(responses=serializers.ModoboaComponentSerializer(many=True)) diff --git a/modoboa/core/handlers.py b/modoboa/core/handlers.py index 12199ab4c..459296d4d 100644 --- a/modoboa/core/handlers.py +++ b/modoboa/core/handlers.py @@ -125,7 +125,9 @@ def check_for_new_versions(sender, include_all: bool, **kwargs) -> list: { "id": "newversionavailable", "text": _("One or more updates are available"), - "level": "info", + "color": "info", + "url": "/admin/information", + "target": "admin", } ] elif include_all: @@ -137,7 +139,9 @@ def check_for_new_versions(sender, include_all: bool, **kwargs) -> list: "id": "deprecatedpasswordscheme", "text": _("You are still using a deprecated password scheme (%s)") % hasher.name, - "level": "warning", + "color": "warning", + "url": "/admin/information", + "target": "admin", } ] return result diff --git a/modoboa/core/utils.py b/modoboa/core/utils.py index ff6a55015..cc8543f81 100644 --- a/modoboa/core/utils.py +++ b/modoboa/core/utils.py @@ -111,6 +111,9 @@ def get_capabilities(): if is_rspamd_installed: rspamd_options = get_rspamd_options() capabilities.update({"rspamd": rspamd_options}) + # Amavis + if "modoboa.amavis" in settings.MODOBOA_APPS: + capabilities.update({"amavis": {}}) # IMAP migration if "modoboa.imap_migration" in settings.MODOBOA_APPS: capabilities.update({"imap_migration": {}}) diff --git a/modoboa/lib/email_utils.py b/modoboa/lib/email_utils.py index 3e28c05aa..b59d0f771 100644 --- a/modoboa/lib/email_utils.py +++ b/modoboa/lib/email_utils.py @@ -334,7 +334,7 @@ def split_mailbox(mailbox, return_extension=False): return (local_part, domain, extension) -def decode(value_bytes, encoding, append_to_error=""): +def decode(value_bytes: bytes, encoding: str, append_to_error: str = "") -> str: """Try to decode the given string.""" assert isinstance(value_bytes, bytes), "value_bytes should be of type bytes" if len(value_bytes) == 0: diff --git a/modoboa/lib/permissions.py b/modoboa/lib/permissions.py index b9af35513..cb5477f91 100644 --- a/modoboa/lib/permissions.py +++ b/modoboa/lib/permissions.py @@ -166,3 +166,10 @@ class CanCreateDomain(permissions.BasePermission): def has_permission(self, request, view): return request.user.has_perm("admin.add_domain") + + +class CanViewDomain(permissions.BasePermission): + """Permissions class to allow users with view_domain right.""" + + def has_permission(self, request, view): + return request.user.has_perm("admin.view_domain") diff --git a/modoboa/lib/test_runners.py b/modoboa/lib/test_runners.py new file mode 100644 index 000000000..f6f2d2734 --- /dev/null +++ b/modoboa/lib/test_runners.py @@ -0,0 +1,29 @@ +"""Custom test runner.""" + +from django.apps import apps +from django.test.runner import DiscoverRunner + + +class UnManagedModelTestRunner(DiscoverRunner): + """ + Test runner that automatically makes all unmanaged models in your Django + project managed for the duration of the test run. + Many thanks to the Caktus Group: http://bit.ly/1N8TcHW + """ + + unmanaged_models = [] + + def setup_test_environment(self, *args, **kwargs): + """Mark amavis models as managed during testing + During database setup migrations are only run for managed models""" + for m in apps.get_models(): + if m._meta.app_label == "amavis": + self.unmanaged_models.append(m) + m._meta.managed = True + super().setup_test_environment(*args, **kwargs) + + def teardown_test_environment(self, *args, **kwargs): + """Revert modoboa_amavis models to unmanaged""" + super().teardown_test_environment(*args, **kwargs) + for m in self.unmanaged_models: + m._meta.managed = False diff --git a/modoboa/locale/cs_CZ/LC_MESSAGES/django.po b/modoboa/locale/cs_CZ/LC_MESSAGES/django.po index 19e5ca54d..8d558625d 100644 --- a/modoboa/locale/cs_CZ/LC_MESSAGES/django.po +++ b/modoboa/locale/cs_CZ/LC_MESSAGES/django.po @@ -14,14 +14,12 @@ msgstr "" "POT-Creation-Date: 2025-08-28 09:41+0200\n" "PO-Revision-Date: 2020-05-06 11:16+0000\n" "Last-Translator: Antoine Nguyen \n" -"Language-Team: Czech (Czech Republic) (http://www.transifex.com/tonio/" -"modoboa/language/cs_CZ/)\n" +"Language-Team: Czech (Czech Republic) (http://www.transifex.com/tonio/modoboa/language/cs_CZ/)\n" "Language: cs_CZ\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " -"<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" +"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" #: admin/api/v1/serializers.py:39 msgid "" @@ -188,11 +186,11 @@ msgstr "Platné MX záznamy" #: admin/app_settings.py:37 msgid "" -"A list of IP or network address every MX record should match. A warning will " -"be sent if a record does not respect it." +"A list of IP or network address every MX record should match. A warning will" +" be sent if a record does not respect it." msgstr "" -"Seznam IP nebo síťových adres, které by každý MX záznam měl obsahovat. Pokud " -"toto záznam nerespektuje, bude odesláno varování." +"Seznam IP nebo síťových adres, které by každý MX záznam měl obsahovat. Pokud" +" toto záznam nerespektuje, bude odesláno varování." #: admin/app_settings.py:47 msgid "New domains must use authorized MX(s)" @@ -385,7 +383,8 @@ msgid "Automatic domain/mailbox creation" msgstr "Automatické vytvoření domény/schránky" #: admin/app_settings.py:221 -msgid "Create a domain and a mailbox when an account is automatically created." +msgid "" +"Create a domain and a mailbox when an account is automatically created." msgstr "Při založení účtu automaticky vytvořit doménu a schránku." #: admin/app_settings.py:230 @@ -728,8 +727,8 @@ msgid "" "and ask them to unlist detected IP address(es)." msgstr "" "Reputace domény bude ovlivněna, a je možné, že e-maily přicházející z ní " -"jsou považovány za spam. Měli byste kontaktovat tyto poskytovatele a požádat " -"je o odstranění IP adresy (adres) ze seznamu." +"jsou považovány za spam. Měli byste kontaktovat tyto poskytovatele a požádat" +" je o odstranění IP adresy (adres) ze seznamu." #: admin/templates/admin/notifications/domain_invalid_mx.html:3 #, python-format @@ -776,14 +775,16 @@ msgstr "Obecné" #: autoreply/app_settings.py:18 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "Period between two auto-replies" msgstr "" "Interval v sekundách mezi dvěmi automatickými odpověďmi stejnému příjemci" #: autoreply/app_settings.py:20 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "" "Number of days to wait before sending a new auto-reply to the same recipient" msgstr "" @@ -1015,7 +1016,8 @@ msgstr "Neplatný počet cyklů" msgid "This code is invalid" msgstr "" -#: core/api/v2/serializers.py:503 core/views/auth.py:194 core/views/auth.py:247 +#: core/api/v2/serializers.py:503 core/views/auth.py:194 +#: core/views/auth.py:247 msgid "Please use the following code to recover your Modoboa password: {}" msgstr "" @@ -1071,8 +1073,8 @@ msgid "" "Number of rounds to use (only used by sha256crypt and sha512crypt). Must be " "between 1000 and 999999999, inclusive." msgstr "" -"Počet cyklů (použije se pouze u sha256crypt a sha512crypt). Hodnota musí být " -"mezi 1000 a 999999999 včetně." +"Počet cyklů (použije se pouze u sha256crypt a sha512crypt). Hodnota musí být" +" mezi 1000 a 999999999 včetně." #: core/app_settings.py:73 msgid "Update password scheme at login" @@ -1216,8 +1218,8 @@ msgstr "Správcovské skupiny" #: core/app_settings.py:239 msgid "" -"Members of those LDAP Posix groups will be created as domain administrators. " -"Use ';' characters to separate groups." +"Members of those LDAP Posix groups will be created as domain administrators." +" Use ';' characters to separate groups." msgstr "" "Členové těchto LDAP Posix skupin budou automaticky vytvořeni jako správci " "domény. Pro oddělení skupin použijte ';'." @@ -1406,13 +1408,15 @@ msgstr "Zobrazit vlastní RSS zdroj distributorům a správcům domény" #: core/app_settings.py:506 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed to super admins" msgstr "Zobrazit vlastní RSS zdroj distributorům a správcům domény" #: core/app_settings.py:509 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed also to super administrators" msgstr "Zobrazit vlastní RSS zdroj distributorům a správcům domény" @@ -1532,8 +1536,8 @@ msgstr "Počet dnů pro neaktivní účet" #: core/app_settings.py:686 msgid "" -"Allow the administrator to set a threshold (in days) beyond which an account " -"is considered inactive if the user hasn't logged in" +"Allow the administrator to set a threshold (in days) beyond which an account" +" is considered inactive if the user hasn't logged in" msgstr "" #: core/app_settings.py:695 @@ -1542,8 +1546,8 @@ msgstr "Počet dnů pro neaktivní účet" #: core/app_settings.py:698 msgid "" -"An account with a last login date greater than this threshold (in days) will " -"be considered as inactive" +"An account with a last login date greater than this threshold (in days) will" +" be considered as inactive" msgstr "" "Účet s datem posledního přihlášení starším než práh (ve dnech) bude " "považován za neaktivní" @@ -1619,8 +1623,9 @@ msgstr "" #: core/checks.py:34 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"dovecot.html#dovecot-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-" +"oauth2" msgstr "" #: core/checks.py:41 @@ -1631,8 +1636,9 @@ msgstr "" #: core/checks.py:45 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"radicale.html#radicale-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-" +"oauth2" msgstr "" #: core/constants.py:6 @@ -2240,10 +2246,11 @@ msgstr "Doménové aliasy" #: limits/app_settings.py:52 #, fuzzy -#| msgid "Maximum number of allowed domain aliases for a new administrator" +#| msgid "" +#| "Maximum number of allowed domain aliases for a new administrator" msgid "" -"Maximum number of allowed domain aliases for a new administrator. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain aliases for a new administrator. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "Maximální povolený počet doménových aliasů (nastavení nového správce)" #: limits/app_settings.py:64 @@ -2296,13 +2303,14 @@ msgstr "" #| msgid "" #| "Maximum number of allowed domain administrators for a new administrator" msgid "" -"Maximum number of allowed domain administrators for a new domain. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain administrators for a new domain. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "Maximální povolený počet správců domény (nastavení nového správce)" #: limits/app_settings.py:126 #, fuzzy -#| msgid "Maximum number of allowed domain aliases for a new administrator" +#| msgid "" +#| "Maximum number of allowed domain aliases for a new administrator" msgid "" "Maximum number of allowed domain aliases for a new domain. (0 to deny any " "creation, -1 to allow unlimited creations)" @@ -2320,8 +2328,8 @@ msgstr "Maximální povolený počet schránek (nastavení nového správce)" #, fuzzy #| msgid "Maximum number of allowed aliases for a new administrator" msgid "" -"Maximum number of allowed aliases for a new domain. (0 to deny any creation, " -"-1 to allow unlimited creations)" +"Maximum number of allowed aliases for a new domain. (0 to deny any creation," +" -1 to allow unlimited creations)" msgstr "Maximální povolený počet aliasů (nastavení nového správce)" #: limits/apps.py:16 @@ -2567,8 +2575,8 @@ msgid "" "Generate a new document only when a new account is created. If set to no, a " "new document will be created each time a password is updated." msgstr "" -"Vytvářet nové dokumenty pouze při vytvoření účtu. Je-li nastaveno na hodnotu " -"ne, bude nový dokument vytvořen pokaždé, když dojde ke změne hesla." +"Vytvářet nové dokumenty pouze při vytvoření účtu. Je-li nastaveno na hodnotu" +" ne, bude nový dokument vytvořen pokaždé, když dojde ke změne hesla." #: pdfcredentials/app_settings.py:84 msgid "Customization options" @@ -2696,8 +2704,7 @@ msgid "" "your contacts.\n" msgstr "" "\n" -"Zde si kdykoliv můžete prohlédnout své emaily, vytvořit filtry nebo " -"spravovat Vaše kontakty.\n" +"Zde si kdykoliv můžete prohlédnout své emaily, vytvořit filtry nebo spravovat Vaše kontakty.\n" #: pdfcredentials/documents.py:126 msgid "Please change your password!" @@ -2725,7 +2732,8 @@ msgstr "PDF přístupové údaje" #: pdfcredentials/lib.py:29 #, fuzzy -#| msgid "Failed to create the directory that will contain PDF documents (%s)" +#| msgid "" +#| "Failed to create the directory that will contain PDF documents (%s)" msgid "Can't write to selected directory to store PDF documents" msgstr "Vytváření adresáře pro uložení PDF dokumentů (%s) selhalo " @@ -2807,8 +2815,8 @@ msgstr "" #: rspamd/app_settings.py:21 msgid "" -"Absolute path of the file which contains paths to DKIM private keys. Must be " -"readable by _rspamd group or user." +"Absolute path of the file which contains paths to DKIM private keys. Must be" +" readable by _rspamd group or user." msgstr "" #: rspamd/app_settings.py:30 @@ -2827,8 +2835,8 @@ msgstr "" #: rspamd/app_settings.py:53 msgid "" -"Location of the rspamd dashboard. Can either be a relative URL (eg. '/" -"rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " +"Location of the rspamd dashboard. Can either be a relative URL (eg. " +"'/rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " "guidance on the Super-Admin dashboard." msgstr "" @@ -3095,7 +3103,8 @@ msgid "" "To initiate the password reset process for your %(username)s Modoboa " "account, click the link below:" msgstr "" -"Klikněte na odkaz níže pro resetování hesla vašeho Modoboa účtu %(username)s:" +"Klikněte na odkaz níže pro resetování hesla vašeho Modoboa účtu " +"%(username)s:" #: templates/registration/password_reset_email.html:10 #: templates/registration/password_reset_email_v2.html:10 @@ -3137,8 +3146,8 @@ msgstr "Původní heslo" #: templates/registration/twofactor_code_verify.html:35 msgid "" -"Enter the code from the two-factor app on your mobile device. If you've lost " -"your device, you may enter one of your recovery codes." +"Enter the code from the two-factor app on your mobile device. If you've lost" +" your device, you may enter one of your recovery codes." msgstr "" #: templates/registration/twofactor_code_verify.html:42 @@ -3396,6 +3405,5 @@ msgid "Attachment is too big (limit: %s)" msgstr "Příloha je příliš velká (omezení: %s)" #, fuzzy, python-brace-format -#~| msgid "Connection to IMAP server failed: %s" #~ msgid "Connection to IMAP server failed: {error}" #~ msgstr "Spojení se serverem IMAP selhalo: %s" diff --git a/modoboa/locale/de/LC_MESSAGES/django.po b/modoboa/locale/de/LC_MESSAGES/django.po index 01b272026..a6579f4ff 100644 --- a/modoboa/locale/de/LC_MESSAGES/django.po +++ b/modoboa/locale/de/LC_MESSAGES/django.po @@ -174,7 +174,8 @@ msgstr "Dieses Feld erlaubt nur gültige IP-Adressen (oder Netzwerke)" #: admin/api/v2/serializers.py:292 msgid "Please disable imap migration before disabling this option." -msgstr "Bitte IMAP Migration deaktivieren bevor Sie diese Option deaktivieren." +msgstr "" +"Bitte IMAP Migration deaktivieren bevor Sie diese Option deaktivieren." #: admin/api/v2/serializers.py:305 msgid "Define at least one authorized network / address" @@ -427,7 +428,8 @@ msgstr "Konto Import fehlgeschlagen (%s): ungültiger Quota-Wert" #: admin/lib.py:121 msgid "You are not allowed to import domains" -msgstr "Sie verfügen nicht über ausreichend Rechte, um Domains zu importieren." +msgstr "" +"Sie verfügen nicht über ausreichend Rechte, um Domains zu importieren." #: admin/lib.py:130 msgid "You are not allowed to import domain aliases." diff --git a/modoboa/locale/el_GR/LC_MESSAGES/django.po b/modoboa/locale/el_GR/LC_MESSAGES/django.po index f663d5ff4..a1812ebcf 100644 --- a/modoboa/locale/el_GR/LC_MESSAGES/django.po +++ b/modoboa/locale/el_GR/LC_MESSAGES/django.po @@ -12,8 +12,7 @@ msgstr "" "POT-Creation-Date: 2025-08-28 09:41+0200\n" "PO-Revision-Date: 2017-10-12 19:37+0000\n" "Last-Translator: Kostas Moumouris \n" -"Language-Team: Greek (Greece) (http://www.transifex.com/tonio/modoboa/" -"language/el_GR/)\n" +"Language-Team: Greek (Greece) (http://www.transifex.com/tonio/modoboa/language/el_GR/)\n" "Language: el_GR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -198,8 +197,8 @@ msgstr "Έγκυρα MXς" #: admin/app_settings.py:37 msgid "" -"A list of IP or network address every MX record should match. A warning will " -"be sent if a record does not respect it." +"A list of IP or network address every MX record should match. A warning will" +" be sent if a record does not respect it." msgstr "" "Μια λίστα από IP ή δικτυακές διευθύνσεις που κάθε εγράφη MX πρέπει να " "ταιριάζει. " @@ -213,8 +212,8 @@ msgid "" "Prevent the creation of a new domain if its MX record does not use one of " "the defined addresses." msgstr "" -"Αποτροπή δημιουργίας νέου domain, εάν η εγράφη MX χρησιμοποιεί μια από τις " -"είδη ορισμένες διευθύνσεις." +"Αποτροπή δημιουργίας νέου domain, εάν η εγράφη MX χρησιμοποιεί μια από τις" +" είδη ορισμένες διευθύνσεις." #: admin/app_settings.py:59 #, fuzzy @@ -364,7 +363,8 @@ msgid "" "value specified. A value of 0 means no quota." msgstr "" "Προκαθορισμένο quota γραμματοκιβωτίου (σε MB) που εφαρμόζεται στα νέα " -"domains χωρίς προκαθορισμένες τιμές. Μια τιμή 0 σημαίνει ότι δεν έχει quota. " +"domains χωρίς προκαθορισμένες τιμές. Μια τιμή 0 σημαίνει ότι δεν έχει quota." +" " #: admin/app_settings.py:197 #, fuzzy @@ -385,14 +385,16 @@ msgstr "Αυτόματη αφαίρεση λογαριασμών" #: admin/app_settings.py:209 msgid "When a mailbox is removed, also remove the associated account" msgstr "" -"Όταν αφαιρείται ένα γραμματοκιβώτιο, να αφαιρείται και ο σχετικός λογαριασμός" +"Όταν αφαιρείται ένα γραμματοκιβώτιο, να αφαιρείται και ο σχετικός " +"λογαριασμός" #: admin/app_settings.py:218 msgid "Automatic domain/mailbox creation" msgstr "Αυτόματη δημιουργία domain/γραμματοκιβωτίων" #: admin/app_settings.py:221 -msgid "Create a domain and a mailbox when an account is automatically created." +msgid "" +"Create a domain and a mailbox when an account is automatically created." msgstr "" "Δημιουργία domain και γραμματοκιβωτίου κατά την αυτόματη δημιουργία " "λογαριασμού" @@ -675,8 +677,8 @@ msgstr "" #| msgid "Default mailbox quota cannot be greater than domain quota" msgid "{}: default mailbox quota cannot be greater than domain quota" msgstr "" -"Το προεπιλεγμένο quota για το γραμματοκιβώτιο δεν πρέπει να είναι μεγαλύτερο " -"από το domain quota. " +"Το προεπιλεγμένο quota για το γραμματοκιβώτιο δεν πρέπει να είναι μεγαλύτερο" +" από το domain quota. " #: admin/models/domain_alias.py:36 msgid "The alias name" @@ -739,8 +741,8 @@ msgid "" msgstr "" "Η φήμη (reputation) του domain θα επηρεαστεί και υπάρχει πιθανότητα τα " "μηνύματα ηλεκτρονικού ταχυδρομείου που προέρχονται από αυτό να θεωρούνται " -"ανεπιθύμητα. Θα πρέπει να επικοινωνήσετε με αυτούς τους παροχους και να τους " -"ζητήσετε να σας βγάλουν από τη λίστα ανεπιθύμητων διευθύνσεων IP." +"ανεπιθύμητα. Θα πρέπει να επικοινωνήσετε με αυτούς τους παροχους και να τους" +" ζητήσετε να σας βγάλουν από τη λίστα ανεπιθύμητων διευθύνσεων IP." #: admin/templates/admin/notifications/domain_invalid_mx.html:3 #, python-format @@ -789,7 +791,8 @@ msgstr "Γενικά" #: autoreply/app_settings.py:18 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "Period between two auto-replies" msgstr "" "Χρονικό όριο λήξης σε δευτερόλεπτα μεταξύ δύο αυτόματων απαντήσεων προς τον " @@ -797,7 +800,8 @@ msgstr "" #: autoreply/app_settings.py:20 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "" "Number of days to wait before sending a new auto-reply to the same recipient" msgstr "" @@ -1037,7 +1041,8 @@ msgstr "Λάθος αριθμός επαναλήψεων " msgid "This code is invalid" msgstr "" -#: core/api/v2/serializers.py:503 core/views/auth.py:194 core/views/auth.py:247 +#: core/api/v2/serializers.py:503 core/views/auth.py:194 +#: core/views/auth.py:247 msgid "Please use the following code to recover your Modoboa password: {}" msgstr "" @@ -1236,8 +1241,8 @@ msgstr "Χρήση ασφαλής σύνδεσης" #: core/app_settings.py:221 msgid "Use an SSL/STARTTLS connection to access the LDAP server" msgstr "" -"Χρησιμοποιήστε μια σύνδεση SSL / STARTTLS για να συνδεθήκατε στον διακομιστή " -"LDAP" +"Χρησιμοποιήστε μια σύνδεση SSL / STARTTLS για να συνδεθήκατε στον διακομιστή" +" LDAP" #: core/app_settings.py:228 msgid "Active Directory" @@ -1253,11 +1258,11 @@ msgstr "Groups Διαχειριστών" #: core/app_settings.py:239 msgid "" -"Members of those LDAP Posix groups will be created as domain administrators. " -"Use ';' characters to separate groups." +"Members of those LDAP Posix groups will be created as domain administrators." +" Use ';' characters to separate groups." msgstr "" -"Τα μέλη αυτών των LDAP Posix ομάδων χρηστών θα δημιουργηθούν ως διαχειριστές " -"domain. Χρησιμοποιείστε τον χαραχτήρα ';' για να ξεχωρίσετε τις ομάδες." +"Τα μέλη αυτών των LDAP Posix ομάδων χρηστών θα δημιουργηθούν ως διαχειριστές" +" domain. Χρησιμοποιείστε τον χαραχτήρα ';' για να ξεχωρίσετε τις ομάδες." #: core/app_settings.py:248 msgid "Group type" @@ -1335,8 +1340,8 @@ msgid "" "An optional filter string (e.g. '(objectClass=person)'). In order to be " "valid, it must be enclosed in parentheses." msgstr "" -"Προαιρετικό φίλτρο (π.χ. '(objectClass=person)'). Για να είναι έγκυρο πρέπει " -"να περικλείεται σε παρενθέσεις." +"Προαιρετικό φίλτρο (π.χ. '(objectClass=person)'). Για να είναι έγκυρο πρέπει" +" να περικλείεται σε παρενθέσεις." #: core/app_settings.py:339 msgid "User DN template" @@ -1458,14 +1463,16 @@ msgstr "" #: core/app_settings.py:506 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed to super admins" msgstr "" "Προβολή κατά παραγγελία RSS feed για μεταπωλητές και διαχειριστές τομέα" #: core/app_settings.py:509 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed also to super administrators" msgstr "" "Προβολή κατά παραγγελία RSS feed για μεταπωλητές και διαχειριστές τομέα" @@ -1588,8 +1595,8 @@ msgstr "Όριο απενεργοποιημένων λογαριασμών " #: core/app_settings.py:686 msgid "" -"Allow the administrator to set a threshold (in days) beyond which an account " -"is considered inactive if the user hasn't logged in" +"Allow the administrator to set a threshold (in days) beyond which an account" +" is considered inactive if the user hasn't logged in" msgstr "" #: core/app_settings.py:695 @@ -1598,8 +1605,8 @@ msgstr "Όριο απενεργοποιημένων λογαριασμών " #: core/app_settings.py:698 msgid "" -"An account with a last login date greater than this threshold (in days) will " -"be considered as inactive" +"An account with a last login date greater than this threshold (in days) will" +" be considered as inactive" msgstr "Όριο (σε ημέρες) για να απενεργοποιηθεί ένας Λογαριασμός." #: core/app_settings.py:707 @@ -1673,8 +1680,9 @@ msgstr "" #: core/checks.py:34 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"dovecot.html#dovecot-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-" +"oauth2" msgstr "" #: core/checks.py:41 @@ -1685,8 +1693,9 @@ msgstr "" #: core/checks.py:45 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"radicale.html#radicale-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-" +"oauth2" msgstr "" #: core/constants.py:6 @@ -1825,7 +1834,8 @@ msgstr "" #: core/models.py:150 msgid "Failed to update password: LDAP module not installed" msgstr "" -"Σφάλμα κατά την ενημέρωση του κωδικού: το LDAP module δεν είναι εγκατεστημένο" +"Σφάλμα κατά την ενημέρωση του κωδικού: το LDAP module δεν είναι " +"εγκατεστημένο" #: core/models.py:185 policyd/core.py:117 msgid "account" @@ -2300,8 +2310,8 @@ msgstr "Ψευδώνυμα domain" #: limits/app_settings.py:52 msgid "" -"Maximum number of allowed domain aliases for a new administrator. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain aliases for a new administrator. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Μέγιστος αριθμός από επιτρεπόμενα ψευδώνυμα για domain, για νέους " "διαχειριστές. (Το 0 δεν επιτρέπει κανένα, το -1 επιτρέπει απεριόριστα) " @@ -2336,8 +2346,8 @@ msgid "" "The quota a reseller will be allowed to share between the domains he " "creates. (0 means no quota)" msgstr "" -"To quota που ένας μεταπωλητής επιτρέπεται να μοιράζει μεταξύ των domains που " -"δημιουργεί. (0 σημαίνει χωρίς quota)" +"To quota που ένας μεταπωλητής επιτρέπεται να μοιράζει μεταξύ των domains που" +" δημιουργεί. (0 σημαίνει χωρίς quota)" #: limits/app_settings.py:98 msgid "Default per-domain limits" @@ -2353,8 +2363,8 @@ msgstr "Ενεργοποίηση ή απενεργοποίηση ορίων αν #: limits/app_settings.py:114 msgid "" -"Maximum number of allowed domain administrators for a new domain. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain administrators for a new domain. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Μέγιστος αριθμός από επιτρεπόμενους διαχειριστές domain για ένα νέο domain. " "(0 απαγορεύει την δημιουργία, -1 επιτρέπει άπειρες δημιουργίες)" @@ -2377,8 +2387,8 @@ msgstr "" #: limits/app_settings.py:149 msgid "" -"Maximum number of allowed aliases for a new domain. (0 to deny any creation, " -"-1 to allow unlimited creations)" +"Maximum number of allowed aliases for a new domain. (0 to deny any creation," +" -1 to allow unlimited creations)" msgstr "" "Μέγιστος αριθμός από επιτρεπόμενα ψευδώνυμα για ένα νέο domain. (0 " "απαγορεύει την δημιουργία, -1 επιτρέπει άπειρες δημιουργίες)" @@ -2433,8 +2443,8 @@ msgstr "" #: limits/constants.py:93 msgid "Maximum number of mailbox aliases allowed for this domain." msgstr "" -"Μέγιστος αριθμός από ψευδώνυμα γραμματοκιβωτίων που επιτρέπονται για αυτό το " -"domain. " +"Μέγιστος αριθμός από ψευδώνυμα γραμματοκιβωτίων που επιτρέπονται για αυτό το" +" domain. " #: limits/constants.py:102 msgid "Maximum number of domain admins allowed for this domain." @@ -2751,8 +2761,7 @@ msgid "" "your contacts.\n" msgstr "" "\n" -"Εδώ μπορείτε να προβάλετε τα ηλεκτρονικά σας μηνύματα σε πραγματικό χρόνο, " -"να δημιουργήσετε φίλτρα ή να διαχειριστείτε\n" +"Εδώ μπορείτε να προβάλετε τα ηλεκτρονικά σας μηνύματα σε πραγματικό χρόνο, να δημιουργήσετε φίλτρα ή να διαχειριστείτε\n" " τις επαφές σας.\n" #: pdfcredentials/documents.py:126 @@ -2783,18 +2792,19 @@ msgstr "Διαπιστευτήρια PDF" #: pdfcredentials/lib.py:29 #, fuzzy -#| msgid "Failed to create the directory that will contain PDF documents (%s)" +#| msgid "" +#| "Failed to create the directory that will contain PDF documents (%s)" msgid "Can't write to selected directory to store PDF documents" msgstr "" -"Σφάλμα στην δημιουργία του καταλόγου αρχείων για την αποθήκευση PDF εγγράφων " -"(%s)" +"Σφάλμα στην δημιουργία του καταλόγου αρχείων για την αποθήκευση PDF εγγράφων" +" (%s)" #: pdfcredentials/lib.py:35 #, python-format msgid "Failed to create the directory that will contain PDF documents (%s)" msgstr "" -"Σφάλμα στην δημιουργία του καταλόγου αρχείων για την αποθήκευση PDF εγγράφων " -"(%s)" +"Σφάλμα στην δημιουργία του καταλόγου αρχείων για την αποθήκευση PDF εγγράφων" +" (%s)" #: pdfcredentials/lib.py:82 msgid "" @@ -2865,8 +2875,8 @@ msgstr "" #: rspamd/app_settings.py:21 msgid "" -"Absolute path of the file which contains paths to DKIM private keys. Must be " -"readable by _rspamd group or user." +"Absolute path of the file which contains paths to DKIM private keys. Must be" +" readable by _rspamd group or user." msgstr "" #: rspamd/app_settings.py:30 @@ -2885,8 +2895,8 @@ msgstr "" #: rspamd/app_settings.py:53 msgid "" -"Location of the rspamd dashboard. Can either be a relative URL (eg. '/" -"rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " +"Location of the rspamd dashboard. Can either be a relative URL (eg. " +"'/rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " "guidance on the Super-Admin dashboard." msgstr "" @@ -3119,8 +3129,8 @@ msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" -"Ο σύνδεσμος για την επαναφορά του κωδικού ήταν άκυρος, πιθανότατα γιατί είχε " -"είδη χρησιμοποιηθεί. Παρακαλώ ζητήστε μια νέα επαναφορά κωδικού." +"Ο σύνδεσμος για την επαναφορά του κωδικού ήταν άκυρος, πιθανότατα γιατί είχε" +" είδη χρησιμοποιηθεί. Παρακαλώ ζητήστε μια νέα επαναφορά κωδικού." #: templates/registration/password_reset_confirm_code.html:22 msgid "Enter the code you've just received by SMS" @@ -3144,8 +3154,8 @@ msgstr "" "Σας έχουμε αποστείλει με ηλεκτρονικό ταχυδρομείο οδηγίες για να ορίσετε των " "κωδικό σας, εφόσον υπάρχει ένας λογαριασμός με το διεύθυνση ηλεκτρονικής " "αλληλογραφίας που καταχωρήσατε. Θα πρέπει να το λάβετε σύντομα.

Εάν " -"δεν λάβατε το email, παρακαλώ συγουρευτείτε ότι εισάγατε σωστά την διεύθυνση " -"σας και ελέγξτε τον φάκελο τον Spam σας.

" +"δεν λάβατε το email, παρακαλώ συγουρευτείτε ότι εισάγατε σωστά την διεύθυνση" +" σας και ελέγξτε τον φάκελο τον Spam σας.

" #: templates/registration/password_reset_email.html:4 #: templates/registration/password_reset_email_v2.html:4 @@ -3197,8 +3207,8 @@ msgstr "Παλιός κωδικός" #: templates/registration/twofactor_code_verify.html:35 msgid "" -"Enter the code from the two-factor app on your mobile device. If you've lost " -"your device, you may enter one of your recovery codes." +"Enter the code from the two-factor app on your mobile device. If you've lost" +" your device, you may enter one of your recovery codes." msgstr "" #: templates/registration/twofactor_code_verify.html:42 @@ -3459,6 +3469,5 @@ msgid "Attachment is too big (limit: %s)" msgstr "Το συνημμένο είναι πολύ μεγάλο (όριο: %s)" #, fuzzy, python-brace-format -#~| msgid "Connection to IMAP server failed: %s" #~ msgid "Connection to IMAP server failed: {error}" #~ msgstr "Η σύνδεση στο διακομιστή IMAP απέτυχε:%s" diff --git a/modoboa/locale/es/LC_MESSAGES/django.po b/modoboa/locale/es/LC_MESSAGES/django.po index b0687a062..a45dbc57f 100644 --- a/modoboa/locale/es/LC_MESSAGES/django.po +++ b/modoboa/locale/es/LC_MESSAGES/django.po @@ -19,8 +19,7 @@ msgstr "" "POT-Creation-Date: 2025-08-28 09:41+0200\n" "PO-Revision-Date: 2020-05-06 11:16+0000\n" "Last-Translator: Antoine Nguyen \n" -"Language-Team: Spanish (http://www.transifex.com/tonio/modoboa/language/" -"es/)\n" +"Language-Team: Spanish (http://www.transifex.com/tonio/modoboa/language/es/)\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -205,8 +204,8 @@ msgstr "MX Validos" #: admin/app_settings.py:37 msgid "" -"A list of IP or network address every MX record should match. A warning will " -"be sent if a record does not respect it." +"A list of IP or network address every MX record should match. A warning will" +" be sent if a record does not respect it." msgstr "" "Una lista de IP o dirección de red, cada registro MX debe coincidir. Se " "enviará una advertencia si un registro no la respeta." @@ -323,8 +322,9 @@ msgid "" "Default quota (in MB) applied to freshly created domains with no value " "specified. A value of 0 means no quota." msgstr "" -"Cuota por defecto (en MB) que se aplica a la creación de nuevos dominios que " -"no tuvieran un valor especificado. Un valor de 0 significa que no hay cuota." +"Cuota por defecto (en MB) que se aplica a la creación de nuevos dominios que" +" no tuvieran un valor especificado. Un valor de 0 significa que no hay " +"cuota." #: admin/app_settings.py:156 #, fuzzy @@ -396,8 +396,10 @@ msgid "Automatic domain/mailbox creation" msgstr "Creación automática de dominio/buzón" #: admin/app_settings.py:221 -msgid "Create a domain and a mailbox when an account is automatically created." -msgstr "Crear un dominio y un buzón cuando se cree automáticamente una cuenta." +msgid "" +"Create a domain and a mailbox when an account is automatically created." +msgstr "" +"Crear un dominio y un buzón cuando se cree automáticamente una cuenta." #: admin/app_settings.py:230 msgid "Create an alias when a mailbox is renamed" @@ -726,8 +728,8 @@ msgid "" "Modoboa detected that domain %(domain)s is listed by the following DNSBL " "providers:" msgstr "" -"Modoboa detectó que el(los) dominio(s) %(domain)s está(n) listado(s) por los " -"siguientes proveedores DNSBL:" +"Modoboa detectó que el(los) dominio(s) %(domain)s está(n) listado(s) por los" +" siguientes proveedores DNSBL:" #: admin/templates/admin/notifications/domain_in_dnsbl.html:11 msgid "" @@ -736,16 +738,16 @@ msgid "" "and ask them to unlist detected IP address(es)." msgstr "" "La reputación de los dominios se verán afectados y existe la posibilidad de " -"que los mensajes de correo electrónico procedentes de él se consideren spam. " -"Debe comunicarse con estos proveedores y pedirles que elimine la(s) " +"que los mensajes de correo electrónico procedentes de él se consideren spam." +" Debe comunicarse con estos proveedores y pedirles que elimine la(s) " "dirección(es) IP detectada(s)." #: admin/templates/admin/notifications/domain_invalid_mx.html:3 #, python-format msgid "Modoboa detected that domain %(domain)s has no valid MX record:" msgstr "" -"Modoboa detectó que el(los) dominio(s) %(domain)s está(n) listado(s) por los " -"siguientes proveedores DNSBL:" +"Modoboa detectó que el(los) dominio(s) %(domain)s está(n) listado(s) por los" +" siguientes proveedores DNSBL:" #: admin/tests/test_mx.py:155 #, fuzzy, python-format @@ -788,13 +790,15 @@ msgstr "General" #: autoreply/app_settings.py:18 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "Period between two auto-replies" msgstr "Tiempo en segundos entre dos autorespuestas para el mismo emisor" #: autoreply/app_settings.py:20 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "" "Number of days to wait before sending a new auto-reply to the same recipient" msgstr "Tiempo en segundos entre dos autorespuestas para el mismo emisor" @@ -1029,7 +1033,8 @@ msgstr "Número de rondas inváldo" msgid "This code is invalid" msgstr "" -#: core/api/v2/serializers.py:503 core/views/auth.py:194 core/views/auth.py:247 +#: core/api/v2/serializers.py:503 core/views/auth.py:194 +#: core/views/auth.py:247 msgid "Please use the following code to recover your Modoboa password: {}" msgstr "" @@ -1085,8 +1090,8 @@ msgid "" "Number of rounds to use (only used by sha256crypt and sha512crypt). Must be " "between 1000 and 999999999, inclusive." msgstr "" -"Número de rondas a usar (sólo utilizado por sha256crypr y sha512crypt). Debe " -"estar entre 1000 y 999999999, incluído." +"Número de rondas a usar (sólo utilizado por sha256crypr y sha512crypt). Debe" +" estar entre 1000 y 999999999, incluído." #: core/app_settings.py:73 msgid "Update password scheme at login" @@ -1231,11 +1236,11 @@ msgstr "Administrador de grupos" #: core/app_settings.py:239 msgid "" -"Members of those LDAP Posix groups will be created as domain administrators. " -"Use ';' characters to separate groups." +"Members of those LDAP Posix groups will be created as domain administrators." +" Use ';' characters to separate groups." msgstr "" -"Los miembros de esos grupos Posix LDAP serán creados como administradores de " -"dominio. Use caracteres \";\" para separar los grupos." +"Los miembros de esos grupos Posix LDAP serán creados como administradores de" +" dominio. Use caracteres \";\" para separar los grupos." #: core/app_settings.py:248 msgid "Group type" @@ -1282,8 +1287,8 @@ msgid "" "The distinguished name to use when binding to the LDAP server. Leave empty " "for an anonymous bind" msgstr "" -"El nombre distinguido a usar cuando nos unimos al servidor LDAP. Dejar vació " -"para una unión anónima" +"El nombre distinguido a usar cuando nos unimos al servidor LDAP. Dejar vació" +" para una unión anónima" #: core/app_settings.py:305 core/app_settings.py:367 msgid "Bind password" @@ -1421,7 +1426,8 @@ msgstr "" #: core/app_settings.py:506 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed to super admins" msgstr "" "Mostrar una fuente de agregación RSS personalizada a revendedores y " @@ -1429,7 +1435,8 @@ msgstr "" #: core/app_settings.py:509 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed also to super administrators" msgstr "" "Mostrar una fuente de agregación RSS personalizada a revendedores y " @@ -1553,8 +1560,8 @@ msgstr "Límite para cuentas inactivas" #: core/app_settings.py:686 msgid "" -"Allow the administrator to set a threshold (in days) beyond which an account " -"is considered inactive if the user hasn't logged in" +"Allow the administrator to set a threshold (in days) beyond which an account" +" is considered inactive if the user hasn't logged in" msgstr "" #: core/app_settings.py:695 @@ -1563,8 +1570,8 @@ msgstr "Límite para cuentas inactivas" #: core/app_settings.py:698 msgid "" -"An account with a last login date greater than this threshold (in days) will " -"be considered as inactive" +"An account with a last login date greater than this threshold (in days) will" +" be considered as inactive" msgstr "" "Una cuenta inactiva con una fecha de inicio de sesión previa a este límite " "(en días) se considerará como cuenta inactiva" @@ -1616,9 +1623,9 @@ msgid "" "between summer/winter time (ie the same local time occuring twice due to " "clock change)." msgstr "" -"A fijado USE_TZ como falso, lo que puede llevar a problemas en la transición " -"entre los horarios de verano e invierno (p.ej., que la misma hora exista dos " -"veces debido a cambios del reloj)." +"A fijado USE_TZ como falso, lo que puede llevar a problemas en la transición" +" entre los horarios de verano e invierno (p.ej., que la misma hora exista " +"dos veces debido a cambios del reloj)." #: core/checks.py:19 msgid "Set `USE_TZ = True` in settings.py" @@ -1640,8 +1647,9 @@ msgstr "" #: core/checks.py:34 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"dovecot.html#dovecot-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-" +"oauth2" msgstr "" #: core/checks.py:41 @@ -1652,8 +1660,9 @@ msgstr "" #: core/checks.py:45 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"radicale.html#radicale-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-" +"oauth2" msgstr "" #: core/constants.py:6 @@ -1954,7 +1963,7 @@ msgstr "Nada" #, fuzzy #| msgid "Quantity" msgid "Quarantine" -msgstr "Cantidad" +msgstr "Cuarentena" #: dmarc/models.py:12 #, fuzzy @@ -2258,10 +2267,11 @@ msgstr "Alias del dominio" #: limits/app_settings.py:52 #, fuzzy -#| msgid "Maximum number of allowed domain aliases for a new administrator" +#| msgid "" +#| "Maximum number of allowed domain aliases for a new administrator" msgid "" -"Maximum number of allowed domain aliases for a new administrator. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain aliases for a new administrator. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Número máximo permitido de alias de dominio para un nuevo administrador" @@ -2315,15 +2325,16 @@ msgstr "" #| msgid "" #| "Maximum number of allowed domain administrators for a new administrator" msgid "" -"Maximum number of allowed domain administrators for a new domain. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain administrators for a new domain. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Número máximo de dominios permitido de administradores para un nuevo " "administrador" #: limits/app_settings.py:126 #, fuzzy -#| msgid "Maximum number of allowed domain aliases for a new administrator" +#| msgid "" +#| "Maximum number of allowed domain aliases for a new administrator" msgid "" "Maximum number of allowed domain aliases for a new domain. (0 to deny any " "creation, -1 to allow unlimited creations)" @@ -2342,8 +2353,8 @@ msgstr "Número máximo de mailboxes permitidas para un nuevo administrador" #, fuzzy #| msgid "Maximum number of allowed aliases for a new administrator" msgid "" -"Maximum number of allowed aliases for a new domain. (0 to deny any creation, " -"-1 to allow unlimited creations)" +"Maximum number of allowed aliases for a new domain. (0 to deny any creation," +" -1 to allow unlimited creations)" msgstr "Número máxido de alias permitidos para un nuevo administrador" #: limits/apps.py:16 @@ -2816,8 +2827,8 @@ msgstr "" #: rspamd/app_settings.py:21 msgid "" -"Absolute path of the file which contains paths to DKIM private keys. Must be " -"readable by _rspamd group or user." +"Absolute path of the file which contains paths to DKIM private keys. Must be" +" readable by _rspamd group or user." msgstr "" #: rspamd/app_settings.py:30 @@ -2836,8 +2847,8 @@ msgstr "" #: rspamd/app_settings.py:53 msgid "" -"Location of the rspamd dashboard. Can either be a relative URL (eg. '/" -"rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " +"Location of the rspamd dashboard. Can either be a relative URL (eg. " +"'/rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " "guidance on the Super-Admin dashboard." msgstr "" @@ -2987,7 +2998,8 @@ msgstr "Detener procesamiento" #: sievefilters/lib.py:49 msgid "Connection to MANAGESIEVE server failed, check your configuration" -msgstr "La conexión al servidor MANAGESIEVE falló, verifique su configuración." +msgstr "" +"La conexión al servidor MANAGESIEVE falló, verifique su configuración." #: sievefilters/lib.py:76 #, fuzzy @@ -3088,10 +3100,10 @@ msgid "" "primary address, and check your spam folder.

" msgstr "" "

Si existe una cuenta con el correo electrónico introducido, recibirá en " -"breves instantes instrucciones via email para cambiar su contraseña.

Si no recibe el correo, por favor compruebe que la dirección de correo " -"electrónico introducida es su dirección principal y revise su directorio de " -"correo basura.

" +"breves instantes instrucciones via email para cambiar su " +"contraseña.

Si no recibe el correo, por favor compruebe que la " +"dirección de correo electrónico introducida es su dirección principal y " +"revise su directorio de correo basura.

" #: templates/registration/password_reset_email.html:4 #: templates/registration/password_reset_email_v2.html:4 @@ -3143,8 +3155,8 @@ msgstr "Contraseña anterior" #: templates/registration/twofactor_code_verify.html:35 msgid "" -"Enter the code from the two-factor app on your mobile device. If you've lost " -"your device, you may enter one of your recovery codes." +"Enter the code from the two-factor app on your mobile device. If you've lost" +" your device, you may enter one of your recovery codes." msgstr "" #: templates/registration/twofactor_code_verify.html:42 @@ -3407,6 +3419,5 @@ msgid "Attachment is too big (limit: %s)" msgstr "EL adjunto es demasiado grande (límite: %s)" #, fuzzy, python-brace-format -#~| msgid "Connection to IMAP server failed: %s" #~ msgid "Connection to IMAP server failed: {error}" #~ msgstr "Falló la conexión con el servidor IMAP: %s" diff --git a/modoboa/locale/fr/LC_MESSAGES/django.po b/modoboa/locale/fr/LC_MESSAGES/django.po index 1003449b6..eb35ae766 100644 --- a/modoboa/locale/fr/LC_MESSAGES/django.po +++ b/modoboa/locale/fr/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Antoine Nguyen , 2016-2017,2020,2023-2025 # Christophe CHAUVET , 2018 @@ -19,21 +19,25 @@ msgstr "" "PO-Revision-Date: 2011-07-01 08:15+0000\n" "Last-Translator: Antoine Nguyen , 2016-2017,2020,2023-2025\n" "Language-Team: French (http://app.transifex.com/tonio/modoboa/language/fr/)\n" +"Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: fr\n" "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" #: admin/api/v1/serializers.py:39 msgid "" "Quota shared between mailboxes. Can be expressed in KB, MB (default) or GB. " "A value of 0 means no quota." -msgstr "Quota partagé entre les boîtes aux lettres. Peut être exprimé en Ko, Mo (défaut) ou Go. Une valeur à 0 signifie pas de quota." +msgstr "" +"Quota partagé entre les boîtes aux lettres. Peut être exprimé en Ko, Mo " +"(défaut) ou Go. Une valeur à 0 signifie pas de quota." #: admin/api/v1/serializers.py:46 msgid "Default quota in MB applied to mailboxes. A value of 0 means no quota." -msgstr "Quota par défaut en Mo appliqué aux boîtes aux lettres. Une valeur à 0 signifie pas de quota." +msgstr "" +"Quota par défaut en Mo appliqué aux boîtes aux lettres. Une valeur à 0 " +"signifie pas de quota." #: admin/api/v1/serializers.py:94 msgid "domain alias with this name already exists" @@ -62,7 +66,9 @@ msgstr "Ce champ est requis" #: admin/api/v1/serializers.py:243 msgid "" "List of administered domains (resellers and domain administrators only)." -msgstr "Liste des domaines administrés (revendeurs et administrateurs de domaine seulement)." +msgstr "" +"Liste des domaines administrés (revendeurs et administrateurs de domaine " +"seulement)." #: admin/api/v1/serializers.py:337 admin/api/v2/serializers.py:546 msgid "Not allowed for this role." @@ -125,7 +131,9 @@ msgstr "Doit être un entier positif" #: admin/api/v2/serializers.py:265 msgid "openssl not found, please make sure it is installed." -msgstr "impossible de trouver openssl, veuillez vous assurer vous qu'il est installé." +msgstr "" +"impossible de trouver openssl, veuillez vous assurer vous qu'il est " +"installé." #: admin/api/v2/serializers.py:268 pdfcredentials/api/v2/serializers.py:84 msgid "Directory not found." @@ -137,7 +145,8 @@ msgstr "Ce champ n'autorise que des adresses IP correctes (ou des réseaux)" #: admin/api/v2/serializers.py:292 msgid "Please disable imap migration before disabling this option." -msgstr "Veuillez désactiver la migration IMAP avant de désactiver cette option" +msgstr "" +"Veuillez désactiver la migration IMAP avant de désactiver cette option" #: admin/api/v2/serializers.py:305 msgid "Define at least one authorized network / address" @@ -145,7 +154,8 @@ msgstr "Définissez au moins un réseau / une adresse autorisé" #: admin/api/v2/serializers.py:581 msgid "The left part of an email address can't be more than 64 characters." -msgstr "La partie gauche d'un courriel ne peut pas faire plus de 64 caractères." +msgstr "" +"La partie gauche d'un courriel ne peut pas faire plus de 64 caractères." #: admin/api/v2/serializers.py:603 msgid "A mailbox is required to create aliases." @@ -183,7 +193,10 @@ msgstr "MX(s) valide(s)" msgid "" "A list of IP or network address every MX record should match. A warning will" " be sent if a record does not respect it." -msgstr "Une liste d'adresses IP ou réseau que chaque enregistrement MX doit respecter. Un avertissement sera envoyé dès qu'un enregistrement ne la respecte pas." +msgstr "" +"Une liste d'adresses IP ou réseau que chaque enregistrement MX doit " +"respecter. Un avertissement sera envoyé dès qu'un enregistrement ne la " +"respecte pas." #: admin/app_settings.py:47 msgid "New domains must use authorized MX(s)" @@ -193,7 +206,9 @@ msgstr "Les nouveaux domaines doivent utiliser un ou plusieurs MX autorisés" msgid "" "Prevent the creation of a new domain if its MX record does not use one of " "the defined addresses." -msgstr "Empêcher la création d'un domaine s'il n'a pas au moins un MX utilisant une adresse autorisée." +msgstr "" +"Empêcher la création d'un domaine s'il n'a pas au moins un MX utilisant une " +"adresse autorisée." #: admin/app_settings.py:59 msgid "Enable IPV6 checks" @@ -217,7 +232,9 @@ msgstr "Activer les tests DKIM" #: admin/app_settings.py:80 msgid "Check if every domain with DKIM signin enabled has a valid DNS record" -msgstr "Vérifie que chaque domaine ayant la signature DKIM activée dispose d'un enregistrement DNS valide" +msgstr "" +"Vérifie que chaque domaine ayant la signature DKIM activée dispose d'un " +"enregistrement DNS valide" #: admin/app_settings.py:88 msgid "Enable DMARC checks" @@ -233,7 +250,9 @@ msgstr "Activer les tests d'autoconfiguration" #: admin/app_settings.py:99 msgid "Check if every domain has a valid records for autoconfiguration" -msgstr "Vérifie que chaque domaine a les enregistrements nécessaires à l'autoconfiguration" +msgstr "" +"Vérifie que chaque domaine a les enregistrements nécessaires à " +"l'autoconfiguration" #: admin/app_settings.py:107 msgid "Enable DNSBL checks" @@ -249,7 +268,9 @@ msgstr "Serveur DNS personnalisé" #: admin/app_settings.py:118 msgid "Use a custom DNS server instead of local server configuration" -msgstr "Utiliser un serveur DNS personnalisé à la place de la configuration par défaut du serveur" +msgstr "" +"Utiliser un serveur DNS personnalisé à la place de la configuration par " +"défaut du serveur" #: admin/app_settings.py:126 msgid "DKIM keys storage directory" @@ -260,7 +281,10 @@ msgid "" "Absolute path of the directory where DKIM private keys will be stored. Make " "sure this directory belongs to root user and is not readable by the outside " "world." -msgstr "Chemin absolu du répertoire dans lequel seront stockées les clés DKIM. Assurez vous que ce répertoire appartient à l'utilisateur root et qu'il n'est pas lisible par le monde extérieur." +msgstr "" +"Chemin absolu du répertoire dans lequel seront stockées les clés DKIM. " +"Assurez vous que ce répertoire appartient à l'utilisateur root et qu'il " +"n'est pas lisible par le monde extérieur." #: admin/app_settings.py:137 msgid "Default DKIM key length" @@ -278,7 +302,9 @@ msgstr "Quota de domaine par défaut" msgid "" "Default quota (in MB) applied to freshly created domains with no value " "specified. A value of 0 means no quota." -msgstr "Quota par défaut (en Mo) appliqué aux domaines fraichement créés et ne spécifiant pas de valeur. 0 signifie qu'aucun quota ne sera appliqué." +msgstr "" +"Quota par défaut (en Mo) appliqué aux domaines fraichement créés et ne " +"spécifiant pas de valeur. 0 signifie qu'aucun quota ne sera appliqué." #: admin/app_settings.py:156 msgid "Default domain sending limit" @@ -288,7 +314,9 @@ msgstr "Limite d'envoi par défaut du domaine" msgid "" "Number of messages freshly created domains will be allowed to send per day. " "Leave empty for no limit." -msgstr "Nombre maximal de messages que les nouveaux domaines seront autorisés à envoyer par jour. Laisser vide pour n'indiquer aucune limite." +msgstr "" +"Nombre maximal de messages que les nouveaux domaines seront autorisés à " +"envoyer par jour. Laisser vide pour n'indiquer aucune limite." #: admin/app_settings.py:170 limits/app_settings.py:61 #: limits/app_settings.py:135 limits/constants.py:31 limits/constants.py:84 @@ -303,7 +331,9 @@ msgstr "Gérer les boites sur le système de fichiers" msgid "" "Rename or remove mailboxes on the filesystem when they get renamed or " "removed within Modoboa" -msgstr "Renommer ou supprimer les boites sur le système de fichiers quand elles sont renommées ou supprimées dans Modoboa" +msgstr "" +"Renommer ou supprimer les boites sur le système de fichiers quand elles sont" +" renommées ou supprimées dans Modoboa" #: admin/app_settings.py:186 admin/models/domain.py:36 msgid "Default mailbox quota" @@ -313,7 +343,9 @@ msgstr "Quota par défaut pour une boîte aux lettres" msgid "" "Default mailbox quota (in MB) applied to freshly created domains with no " "value specified. A value of 0 means no quota." -msgstr "Quota par défaut (en Mo) appliqué aux nouveaux domaines quand aucune valeur n'est spécifiée. Une valeur de 0 signifie pas de quota." +msgstr "" +"Quota par défaut (en Mo) appliqué aux nouveaux domaines quand aucune valeur " +"n'est spécifiée. Une valeur de 0 signifie pas de quota." #: admin/app_settings.py:197 msgid "Default mailbox sending limit" @@ -323,7 +355,9 @@ msgstr "Limite d'envoi par défaut d'une boîte aux lettres" msgid "" "Number of messages freshly created mailboxes will be allowed to send per " "day. Leave empty for no limit." -msgstr "Nombre maximal de messages que les nouveaux comptes seront autorisés à envoyer par jour. Laisser vide pour n'indiquer aucune limite." +msgstr "" +"Nombre maximal de messages que les nouveaux comptes seront autorisés à " +"envoyer par jour. Laisser vide pour n'indiquer aucune limite." #: admin/app_settings.py:207 msgid "Automatic account removal" @@ -331,7 +365,9 @@ msgstr "Suppression automatique de compte" #: admin/app_settings.py:209 msgid "When a mailbox is removed, also remove the associated account" -msgstr "Lorsqu'une boite aux lettres est supprimée, supprimer le compte associé en même temps" +msgstr "" +"Lorsqu'une boite aux lettres est supprimée, supprimer le compte associé en " +"même temps" #: admin/app_settings.py:218 msgid "Automatic domain/mailbox creation" @@ -340,7 +376,9 @@ msgstr "Création automatique de domaine/boite aux lettres" #: admin/app_settings.py:221 msgid "" "Create a domain and a mailbox when an account is automatically created." -msgstr "Créer un domaine et une boite aux lettres quand un compte est créé automatiquement." +msgstr "" +"Créer un domaine et une boite aux lettres quand un compte est créé " +"automatiquement." #: admin/app_settings.py:230 msgid "Create an alias when a mailbox is renamed" @@ -348,7 +386,9 @@ msgstr "Créer un alias quand une boite aux lettres est renommée" #: admin/app_settings.py:233 msgid "Create an alias using the old address when a mailbox is renamed." -msgstr "Créer un alias en utilisant l'ancienne adresse quand une boite aux lettres est renommée." +msgstr "" +"Créer un alias en utilisant l'ancienne adresse quand une boite aux lettres " +"est renommée." #: admin/app_settings.py:287 core/api/v2/viewsets.py:174 msgid "Administration" @@ -437,7 +477,9 @@ msgstr "%d objets importés avec succès" #: admin/management/commands/subcommands/_import.py:97 msgid "CSV file is not encoded in UTF-8, attempting to guess encoding" -msgstr "Le fichier CSV n'est pas encodé en UTF-8, tentative de détermination de l'encodage" +msgstr "" +"Le fichier CSV n'est pas encodé en UTF-8, tentative de détermination de " +"l'encodage" #: admin/management/commands/subcommands/_import.py:112 #, python-format @@ -447,13 +489,16 @@ msgstr "Lire le fichier CSV en utilisant l'encodage %(encoding)s" #: admin/management/commands/subcommands/_import.py:123 #, python-format msgid "Unable to decode CSV file using %(encoding)s encoding" -msgstr "Impossible de décoder le fichier CSV en utilisant l'encodage %(encoding)s" +msgstr "" +"Impossible de décoder le fichier CSV en utilisant l'encodage %(encoding)s" #: admin/management/commands/subcommands/_manage_dkim_keys.py:29 msgid "" "DKIM path non-writable (either a permission issue or the directory does not " "exist)" -msgstr "Le chemin d'accès DKIM n'est pas modifiable (soit à cause d'un problème de permission, soit parce que le répertoire n'existe pas)" +msgstr "" +"Le chemin d'accès DKIM n'est pas modifiable (soit à cause d'un problème de " +"permission, soit parce que le répertoire n'existe pas)" #: admin/management/commands/subcommands/_manage_dkim_keys.py:52 msgid "Failed to generate DKIM private key" @@ -572,7 +617,9 @@ msgstr "Activer la signature DKIM" #: admin/models/domain.py:63 msgid "" "If you activate this feature, a DKIM key will be generated for this domain." -msgstr "Si vous activez cette fonctionnalité, une clé DKIM sera générée pour ce domain." +msgstr "" +"Si vous activez cette fonctionnalité, une clé DKIM sera générée pour ce " +"domain." #: admin/models/domain.py:70 msgid "Key length" @@ -593,11 +640,15 @@ msgstr "{} : valeur incorrecte de quota pour ce domaine" #: admin/models/domain.py:343 msgid "{}: invalid default mailbox quota value for domain" -msgstr "{} : la valeur du quota par défaut des boîtes aux lettres est incorrecte pour ce domaine" +msgstr "" +"{} : la valeur du quota par défaut des boîtes aux lettres est incorrecte " +"pour ce domaine" #: admin/models/domain.py:350 msgid "{}: default mailbox quota cannot be greater than domain quota" -msgstr "{} : le quota par défaut de la boîte aux lettres ne peut pas être supérieur au quota du domaine" +msgstr "" +"{} : le quota par défaut de la boîte aux lettres ne peut pas être supérieur " +"au quota du domaine" #: admin/models/domain_alias.py:36 msgid "The alias name" @@ -646,19 +697,26 @@ msgstr "Domaine inexistant" msgid "" "Modoboa detected that domain %(domain)s is listed by the following DNSBL " "providers:" -msgstr "Modoboa a détecté que le domaine %(domain)s est listé par les fournisseurs de DNSBL suivants :" +msgstr "" +"Modoboa a détecté que le domaine %(domain)s est listé par les fournisseurs " +"de DNSBL suivants :" #: admin/templates/admin/notifications/domain_in_dnsbl.html:11 msgid "" "The domain's reputation will be affected and there is a chance that emails " "coming from it are considered as spam. You should contact those providers " "and ask them to unlist detected IP address(es)." -msgstr "La réputation de ce domaine sera affectée et il est probable que les emails venant de lui soient considérés comme du spam. Vous devriez contacter ces fournisseurs pour leur demander de délister la/les adresse(s) détectée(s)." +msgstr "" +"La réputation de ce domaine sera affectée et il est probable que les emails " +"venant de lui soient considérés comme du spam. Vous devriez contacter ces " +"fournisseurs pour leur demander de délister la/les adresse(s) détectée(s)." #: admin/templates/admin/notifications/domain_invalid_mx.html:3 #, python-format msgid "Modoboa detected that domain %(domain)s has no valid MX record:" -msgstr "Modoboa a détecté que le domain %(domain)s n'a aucun enregistrement MX valide :" +msgstr "" +"Modoboa a détecté que le domain %(domain)s n'a aucun enregistrement MX " +"valide :" #: admin/tests/test_mx.py:155 #, python-format @@ -700,7 +758,9 @@ msgstr "Période entre deux réponses automatiques" #: autoreply/app_settings.py:20 msgid "" "Number of days to wait before sending a new auto-reply to the same recipient" -msgstr "Attendre ce nombre de jours avant l'envoi d'une nouvelle réponse automatique au même destinataire" +msgstr "" +"Attendre ce nombre de jours avant l'envoi d'une nouvelle réponse automatique" +" au même destinataire" #: autoreply/app_settings.py:27 autoreply/app_settings.py:36 msgid "Default subject" @@ -709,14 +769,19 @@ msgstr "Sujet par défaut" #: autoreply/app_settings.py:29 msgid "" "Default subject used when an auto-reply message is created automatically" -msgstr "Sujet par défaut utilisé quand un message de réponse automatique est crée automatiquement" +msgstr "" +"Sujet par défaut utilisé quand un message de réponse automatique est crée " +"automatiquement" #: autoreply/app_settings.py:38 #, python-format msgid "" "Default content used when an auto-reply message is created automatically. " "The '%(name)s' macro will be replaced by the user's full name." -msgstr "Contenu par défaut utilisé quand un message de réponse automatique est créé automatiquement. La macro %(name)s sera remplacée par le nom complet de l'utilisateur " +msgstr "" +"Contenu par défaut utilisé quand un message de réponse automatique est créé " +"automatiquement. La macro %(name)s sera remplacée par le nom complet de " +"l'utilisateur " #: autoreply/apps.py:15 msgid "Automatic replies" @@ -753,7 +818,9 @@ msgstr "URL du serveur" #: calendars/app_settings.py:19 msgid "" "The URL of your Radicale server. It will be used to construct calendar URLs." -msgstr "L'URL de votre serveur Radicale. Elle sera utilisée pour construire les URL des calendriers." +msgstr "" +"L'URL de votre serveur Radicale. Elle sera utilisée pour construire les URL " +"des calendriers." #: calendars/app_settings.py:31 msgid "Rights management" @@ -773,7 +840,9 @@ msgstr "Autoriser l'administration des calendriers" #: calendars/app_settings.py:48 msgid "Allow domain administrators to manage user calendars (read and write)" -msgstr "Autoriser les administrateurs de domaine à gérer les calendriers des utilisateurs (lecture et écriture)" +msgstr "" +"Autoriser les administrateurs de domaine à gérer les calendriers des " +"utilisateurs (lecture et écriture)" #: calendars/app_settings.py:60 core/app_settings.py:676 #: rspamd/app_settings.py:45 @@ -787,7 +856,9 @@ msgstr "Taille maximale des fichiers ICS" #: calendars/app_settings.py:68 msgid "" "Maximum size in bytes of imported ICS files (or KB, MB, GB if specified)" -msgstr "Taille maximale en octets des fichiers ICS importés (ou KB, MB, GB si précisé)" +msgstr "" +"Taille maximale en octets des fichiers ICS importés (ou KB, MB, GB si " +"précisé)" #: calendars/app_settings.py:89 core/api/v2/viewsets.py:195 msgid "Calendars" @@ -813,7 +884,9 @@ msgstr "Synchroniser le carnet d'adresse en utilisant CardDAV" msgid "" "Choose to synchronize or not your address book using CardDAV. You will be " "able to access your contacts from the outside." -msgstr "Choisir de synchroniser ou non votre carnet d'adresse en utilisant CardDAV. Vous pourrez ainsi accéder à vos contacts depuis l'extérieur." +msgstr "" +"Choisir de synchroniser ou non votre carnet d'adresse en utilisant CardDAV. " +"Vous pourrez ainsi accéder à vos contacts depuis l'extérieur." #: contacts/app_settings.py:29 msgid "Synchronization frequency" @@ -874,7 +947,9 @@ msgstr "2FA est déjà activf" #: core/api/v1/serializers.py:86 #, python-format msgid "Failed TFA settings editing attempt from '%(addr)s' as user '%(user)s'" -msgstr "Echec de modification des paramètres TFA depuis '%(addr)s' en tant qu'utilisateur '%(user)s'" +msgstr "" +"Echec de modification des paramètres TFA depuis '%(addr)s' en tant " +"qu'utilisateur '%(user)s'" #: core/api/v1/serializers.py:91 core/api/v2/serializers.py:445 msgid "Invalid password" @@ -908,7 +983,9 @@ msgstr "Ce code n'est pas valide" #: core/api/v2/serializers.py:503 core/views/auth.py:194 #: core/views/auth.py:247 msgid "Please use the following code to recover your Modoboa password: {}" -msgstr "Veuillez utiliser le code suivant pour récupérer votre mot de passe Modoboa : {}" +msgstr "" +"Veuillez utiliser le code suivant pour récupérer votre mot de passe Modoboa " +": {}" #: core/api/v2/viewsets.py:176 msgid "Administration console" @@ -945,7 +1022,8 @@ msgstr "Méthode de chiffrement par défaut" #: core/app_settings.py:54 msgid "Scheme used to crypt mailbox passwords" -msgstr "Méthode utilisée pour chiffrer les mots de passe des boites aux lettres" +msgstr "" +"Méthode utilisée pour chiffrer les mots de passe des boites aux lettres" #: core/app_settings.py:61 msgid "Rounds" @@ -955,15 +1033,20 @@ msgstr "Tours" msgid "" "Number of rounds to use (only used by sha256crypt and sha512crypt). Must be " "between 1000 and 999999999, inclusive." -msgstr "Nombre de tours à utiliser (sha256crypt et sha512crypt uniquement). Doit être compris entre 1000 et 999999999 inclue." +msgstr "" +"Nombre de tours à utiliser (sha256crypt et sha512crypt uniquement). Doit " +"être compris entre 1000 et 999999999 inclue." #: core/app_settings.py:73 msgid "Update password scheme at login" -msgstr "Mettre à jour la méthode de chiffrement du mot de passe à la connexion" +msgstr "" +"Mettre à jour la méthode de chiffrement du mot de passe à la connexion" #: core/app_settings.py:76 msgid "Update user password at login to use the default password scheme" -msgstr "Mise à jour du mot de passe utilisateur lors d'une connexion pour utiliser la méthode de chiffrement par défaut" +msgstr "" +"Mise à jour du mot de passe utilisateur lors d'une connexion pour utiliser " +"la méthode de chiffrement par défaut" #: core/app_settings.py:84 msgid "Default password" @@ -987,7 +1070,9 @@ msgstr "Autoriser les caractères spéciaux pour les mots de passe aléatoires" #: core/app_settings.py:106 msgid "Enable special characters in randomly generated passwords." -msgstr "Activer les caractères spéciaux dans les mots de passe générés aléatoirement." +msgstr "" +"Activer les caractères spéciaux dans les mots de passe générés " +"aléatoirement." #: core/app_settings.py:114 msgid "Update password service URL" @@ -998,7 +1083,10 @@ msgid "" "The URL of an external page where users will be able to update their " "password. It applies only to non local users, ie. those automatically " "created after a successful external authentication (LDAP, SMTP)." -msgstr "L'URL d'une page externe où les utilisateurs pourront mettre à jour leur mot de passe. Elle s'applique uniquement aux utilisateurs non locaux, c'est à dire ceux créés après une authentification externe réussie (LDAP, SMTP)." +msgstr "" +"L'URL d'une page externe où les utilisateurs pourront mettre à jour leur mot" +" de passe. Elle s'applique uniquement aux utilisateurs non locaux, c'est à " +"dire ceux créés après une authentification externe réussie (LDAP, SMTP)." #: core/app_settings.py:126 msgid "Password recovery announcement" @@ -1006,7 +1094,9 @@ msgstr "Annonce pour la récupération de mot de passe" #: core/app_settings.py:128 msgid "A temporary message that will be displayed on the reset password page." -msgstr "Un message temporaire qui sera affiché sur la page de récupération de mot de passe" +msgstr "" +"Un message temporaire qui sera affiché sur la page de récupération de mot de" +" passe" #: core/app_settings.py:137 msgid "Enable password recovery by SMS" @@ -1014,7 +1104,9 @@ msgstr "Activer la récupération de mot de passe par SMS" #: core/app_settings.py:140 msgid "Enable password recovery by SMS for users who filled a phone number." -msgstr "Activer la récupération de mot de passe par SMS pour les utilisateurs ayant renseigné un numéro de téléphone" +msgstr "" +"Activer la récupération de mot de passe par SMS pour les utilisateurs ayant " +"renseigné un numéro de téléphone" #: core/app_settings.py:148 msgid "SMS provider" @@ -1022,7 +1114,8 @@ msgstr "Fournisseur SMS" #: core/app_settings.py:151 msgid "Choose a provider to send password recovery SMS" -msgstr "Choisir un fournisseur pour envoyer les SMS de récupération de mot de passe" +msgstr "" +"Choisir un fournisseur pour envoyer les SMS de récupération de mot de passe" #: core/app_settings.py:163 msgid "LDAP" @@ -1055,7 +1148,9 @@ msgstr "Activer le serveur secondaire (secours)" #: core/app_settings.py:191 msgid "" "Enable a secondary LDAP server which will be used if the primary one fails" -msgstr "Activer un serveur LDAP secondaire qui sera utilisé si le principal est en échec" +msgstr "" +"Activer un serveur LDAP secondaire qui sera utilisé si le principal est en " +"échec" #: core/app_settings.py:199 msgid "Secondary server address" @@ -1098,7 +1193,10 @@ msgstr "Groupes administrateurs" msgid "" "Members of those LDAP Posix groups will be created as domain administrators." " Use ';' characters to separate groups." -msgstr "Les membres de ces groupes LDAP (Posix) seront créés en tant qu'administrateurs de domaine. Utiliser des caractères ';' pour séparer les groupes." +msgstr "" +"Les membres de ces groupes LDAP (Posix) seront créés en tant " +"qu'administrateurs de domaine. Utiliser des caractères ';' pour séparer les " +"groupes." #: core/app_settings.py:248 msgid "Group type" @@ -1114,7 +1212,9 @@ msgstr "Base de recherche des groupes" #: core/app_settings.py:259 msgid "The distinguished name of the search base used to find groups" -msgstr "Le nom distinctif de la base de recherche utilisée pour rechercher les groupes" +msgstr "" +"Le nom distinctif de la base de recherche utilisée pour rechercher les " +"groupes" #: core/app_settings.py:267 msgid "Password attribute" @@ -1144,7 +1244,9 @@ msgstr "ND de connexion" msgid "" "The distinguished name to use when binding to the LDAP server. Leave empty " "for an anonymous bind" -msgstr "Le nom distinctif à utiliser pour les connections au serveur LDAP. Laisser vide pour une connexion anonyme" +msgstr "" +"Le nom distinctif à utiliser pour les connections au serveur LDAP. Laisser " +"vide pour une connexion anonyme" #: core/app_settings.py:305 core/app_settings.py:367 msgid "Bind password" @@ -1152,7 +1254,9 @@ msgstr "Mot de passe de connexion" #: core/app_settings.py:307 core/app_settings.py:369 msgid "The password to use when binding to the LDAP server (with 'Bind DN')" -msgstr "Le mot de passe à utiliser pour les connections au serveur LDAP (avec 'ND de connexion')" +msgstr "" +"Le mot de passe à utiliser pour les connections au serveur LDAP (avec 'ND de" +" connexion')" #: core/app_settings.py:317 core/app_settings.py:422 msgid "Users search base" @@ -1160,7 +1264,9 @@ msgstr "Base de recherche des utilisateurs" #: core/app_settings.py:319 core/app_settings.py:424 msgid "The distinguished name of the search base used to find users" -msgstr "Le nom distinctif de la base de recherche utilisée pour rechercher les utilisateurs" +msgstr "" +"Le nom distinctif de la base de recherche utilisée pour rechercher les " +"utilisateurs" #: core/app_settings.py:328 core/app_settings.py:433 msgid "Search filter" @@ -1170,7 +1276,9 @@ msgstr "Filtre de recherche" msgid "" "An optional filter string (e.g. '(objectClass=person)'). In order to be " "valid, it must be enclosed in parentheses." -msgstr "Un filtre de recherche optionel (ex. '(objectClass=person)'). Pour être valide, il doit être entouré de parenthèses." +msgstr "" +"Un filtre de recherche optionel (ex. '(objectClass=person)'). Pour être " +"valide, il doit être entouré de parenthèses." #: core/app_settings.py:339 msgid "User DN template" @@ -1181,7 +1289,9 @@ msgstr "Modèle de nom distinctif utilisateur" msgid "" "The template used to construct a user's DN. It should contain one " "placeholder (ie. %(user)s)" -msgstr "Le modèle utilisé pour construire le nom distinctif d'un utilisateur. Il doit contenir un espace réservé (ex. %(user)s)" +msgstr "" +"Le modèle utilisé pour construire le nom distinctif d'un utilisateur. Il " +"doit contenir un espace réservé (ex. %(user)s)" #: core/app_settings.py:350 msgid "LDAP synchronization settings" @@ -1194,7 +1304,9 @@ msgstr "Activer l'export LDAP" #: core/app_settings.py:380 msgid "" "Enable automatic synchronization between local database and LDAP directory" -msgstr "Activer la synchronisation automatique entre la base de données locale et l'annuaire LDAP" +msgstr "" +"Activer la synchronisation automatique entre la base de données locale et " +"l'annuaire LDAP" #: core/app_settings.py:389 msgid "Delete remote LDAP account when local account is deleted" @@ -1204,7 +1316,9 @@ msgstr "Supprimer un compte LDAP distant lorsqu'un compte local est supprimé" msgid "" "Delete remote LDAP account when local account is deleted, otherwise it will " "be disabled." -msgstr "Supprimer un compte LDAP distant lorsqu'un compte local supprimé, autrement il sera désactivé." +msgstr "" +"Supprimer un compte LDAP distant lorsqu'un compte local supprimé, autrement " +"il sera désactivé." #: core/app_settings.py:401 msgid "Account DN template" @@ -1215,7 +1329,9 @@ msgstr "Modèle des DN de compte" msgid "" "The template used to construct an account's DN. It should contain one " "placeholder (ie. %(user)s)" -msgstr "Le modèle utilisé pour construite un DN de compte (nom unique). Il doit contenir un emplacement (c'est à dire 1%(user)s)" +msgstr "" +"Le modèle utilisé pour construite un DN de compte (nom unique). Il doit " +"contenir un emplacement (c'est à dire 1%(user)s)" #: core/app_settings.py:412 msgid "Enable import from LDAP" @@ -1223,7 +1339,9 @@ msgstr "Activer l'import LDAP" #: core/app_settings.py:414 msgid "Enable account synchronization from LDAP directory to local database" -msgstr "Activer la synchronisation des comptes entre un annuaire LDAP et la base de données locale" +msgstr "" +"Activer la synchronisation des comptes entre un annuaire LDAP et la base de " +"données locale" #: core/app_settings.py:444 msgid "Username attribute" @@ -1231,7 +1349,8 @@ msgstr "Attribut nom d'utilisateur" #: core/app_settings.py:446 msgid "The name of the LDAP attribute where the username can be found." -msgstr "Le nom de l'attribut LDAP dans lequel le nom d'utilisateur peut être trouvé." +msgstr "" +"Le nom de l'attribut LDAP dans lequel le nom d'utilisateur peut être trouvé." #: core/app_settings.py:455 msgid "Enable Dovecot LDAP sync" @@ -1239,7 +1358,9 @@ msgstr "Activer la synchronisation LDAP pour Dovecot" #: core/app_settings.py:457 msgid "LDAP authentication settings will be applied to Dovecot configuration." -msgstr "Les paramètres d'authentification LDAP seront appliqués à la configuration de Dovecot." +msgstr "" +"Les paramètres d'authentification LDAP seront appliqués à la configuration " +"de Dovecot." #: core/app_settings.py:465 msgid "Dovecot LDAP config file" @@ -1248,7 +1369,9 @@ msgstr "Fichier de configuration Dovecot pour LDAP" #: core/app_settings.py:467 msgid "" "Location of the configuration file which contains Dovecot LDAP settings." -msgstr "Emplacement du fichier de configuration qui contient les paramètres LDAP de Dovecot" +msgstr "" +"Emplacement du fichier de configuration qui contient les paramètres LDAP de " +"Dovecot" #: core/app_settings.py:480 msgid "Dashboard" @@ -1268,7 +1391,9 @@ msgstr "Flux RSS personnalisé" #: core/app_settings.py:497 msgid "Display custom RSS feed to resellers and domain administrators" -msgstr "Afficher un flux RSS personnalisé aux revendeurs et administrateurs de domaine" +msgstr "" +"Afficher un flux RSS personnalisé aux revendeurs et administrateurs de " +"domaine" #: core/app_settings.py:506 msgid "Display custom RSS feed to super admins" @@ -1284,7 +1409,9 @@ msgstr "Masquer le widget des fonctionnalités" #: core/app_settings.py:519 msgid "Hide features widget for resellers and domain administrators" -msgstr "Masquer le widget des fonctionnalités pour les revendeurs et les administrateurs de domaine" +msgstr "" +"Masquer le widget des fonctionnalités pour les revendeurs et les " +"administrateurs de domaine" #: core/app_settings.py:531 msgid "Theme" @@ -1298,7 +1425,9 @@ msgstr "Couleur principale du thème" msgid "" "Primary color of the theme (hexadecimal code), used as the background color " "of the left menu for example" -msgstr "Couleur principale du thème (code hexadécimal), utilisée par exemple comme couleur de fond dans le menu de gauche" +msgstr "" +"Couleur principale du thème (code hexadécimal), utilisée par exemple comme " +"couleur de fond dans le menu de gauche" #: core/app_settings.py:548 msgid "Primary theme color (lighter version)" @@ -1320,7 +1449,9 @@ msgstr "Couleur secondaire du thème" msgid "" "Secondary color of the theme (hexadecimal code), used as the background " "color of some buttons for example" -msgstr "Couleur secondaire du thème (code hexadécimal), utilisée par exemple comme couleur de fond de certains boutons" +msgstr "" +"Couleur secondaire du thème (code hexadécimal), utilisée par exemple comme " +"couleur de fond de certains boutons" #: core/app_settings.py:581 msgid "Labels theme color" @@ -1364,7 +1495,9 @@ msgstr "Envoyer un email quand de nouvelles versions sont trouvées" #: core/app_settings.py:644 msgid "Send an email to notify admins about new versions" -msgstr "Envoyer un email pour notifier les administrateurs à propos des nouvelles versions" +msgstr "" +"Envoyer un email pour notifier les administrateurs à propos des nouvelles " +"versions" #: core/app_settings.py:651 sievefilters/constants.py:25 msgid "Recipient" @@ -1380,7 +1513,9 @@ msgstr "Envoyer des statistiques" #: core/app_settings.py:664 msgid "Send statistics to Modoboa public API (counters and used extensions)" -msgstr "Envoyer des statistiques à l'API publique de Modoboa (compteurs et extensions utilisées)" +msgstr "" +"Envoyer des statistiques à l'API publique de Modoboa (compteurs et " +"extensions utilisées)" #: core/app_settings.py:683 msgid "Enable inactive account tracking" @@ -1390,7 +1525,9 @@ msgstr "Activer le suivi des comptes inactifs" msgid "" "Allow the administrator to set a threshold (in days) beyond which an account" " is considered inactive if the user hasn't logged in" -msgstr "Autoriser les administrateurs à définir in seuil (en jours) à partir duquel un compte est considéré comme inactif si l'utilisateur ne s'est pas connecté" +msgstr "" +"Autoriser les administrateurs à définir in seuil (en jours) à partir duquel " +"un compte est considéré comme inactif si l'utilisateur ne s'est pas connecté" #: core/app_settings.py:695 msgid "Inactive account threshold" @@ -1400,7 +1537,9 @@ msgstr "Seuil des comptes inactifs" msgid "" "An account with a last login date greater than this threshold (in days) will" " be considered as inactive" -msgstr "Un compte avec une date de dernière connexion antérieure à ce seuil sera considéré comme inactif" +msgstr "" +"Un compte avec une date de dernière connexion antérieure à ce seuil sera " +"considéré comme inactif" #: core/app_settings.py:707 msgid "Top notifications check interval" @@ -1424,7 +1563,8 @@ msgstr "Durée de rétention dans l'historique des messages" #: core/app_settings.py:730 msgid "Retention time (in days) of a message in the message history section" -msgstr "Durée de rétention (en jours) d'un message dans l'historique des messages" +msgstr "" +"Durée de rétention (en jours) d'un message dans l'historique des messages" #: core/app_settings.py:737 msgid "Items per page" @@ -1440,14 +1580,18 @@ msgstr "Redirection par défaut" #: core/app_settings.py:748 msgid "The default redirection used when no application is specified" -msgstr "La redirection appliquée par défaut quand aucune application n'est spécifiée" +msgstr "" +"La redirection appliquée par défaut quand aucune application n'est spécifiée" #: core/checks.py:15 msgid "" "You have USE_TZ set to False, this may result in issues during transitions " "between summer/winter time (ie the same local time occuring twice due to " "clock change)." -msgstr "Vous avez défini USE_TZ à False, ce qui peut entraîner des problèmes lors des transitions entre l'heure d'été et l'heure d'hiver (c.-à-d. La même heure locale se produisant deux fois en raison d'un changement d'horloge)." +msgstr "" +"Vous avez défini USE_TZ à False, ce qui peut entraîner des problèmes lors " +"des transitions entre l'heure d'été et l'heure d'hiver (c.-à-d. La même " +"heure locale se produisant deux fois en raison d'un changement d'horloge)." #: core/checks.py:19 msgid "Set `USE_TZ = True` in settings.py" @@ -1459,33 +1603,45 @@ msgstr "La méthode de chiffrement que vous utilisez est obsolète." #: core/checks.py:24 msgid "Upgrade your password scheme to a stronger one" -msgstr "Mettez à jour votre méthode de chiffrement de mot de passe vers une plus sûre" +msgstr "" +"Mettez à jour votre méthode de chiffrement de mot de passe vers une plus " +"sûre" #: core/checks.py:30 msgid "" "Oauth2 application for dovecot has not been found. Webmail and some other " "features won't be available." -msgstr "Application Oauth2 non trouvée pour dovecot. Le webmail ainsi que d'autres fonctionnalités ne seront pas disponibles." +msgstr "" +"Application Oauth2 non trouvée pour dovecot. Le webmail ainsi que d'autres " +"fonctionnalités ne seront pas disponibles." #: core/checks.py:34 msgid "" "Visit : " "https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-" "oauth2" -msgstr "Visiter : https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-oauth2" +msgstr "" +"Visiter : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-" +"oauth2" #: core/checks.py:41 msgid "" "Oauth2 application for radicale has not been found. Calendar and some other " "features won't be available." -msgstr "Application Oauth2 pour Radicale non trouvée. Le calendrier ainsi que d'autres fonctionnalités ne seront pas disponibles." +msgstr "" +"Application Oauth2 pour Radicale non trouvée. Le calendrier ainsi que " +"d'autres fonctionnalités ne seront pas disponibles." #: core/checks.py:45 msgid "" "Visit : " "https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-" "oauth2" -msgstr "Consulter : https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-oauth2" +msgstr "" +"Consulter : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-" +"oauth2" #: core/constants.py:6 msgid "Simple user" @@ -1572,7 +1728,9 @@ msgstr "Une ou plusieurs mises à jour sont disponibles!" #: core/handlers.py:138 #, python-format msgid "You are still using a deprecated password scheme (%s)" -msgstr "Vous utilisez encore une méthode de chiffrement de mot de passe obsolète (%s)" +msgstr "" +"Vous utilisez encore une méthode de chiffrement de mot de passe obsolète " +"(%s)" #: core/management/commands/communicate_with_public_api.py:42 msgid "[modoboa] Update(s) available" @@ -1612,7 +1770,8 @@ msgstr "Une adresse e-mail alternative ou de secours." #: core/models.py:150 msgid "Failed to update password: LDAP module not installed" -msgstr "Echec de la mise à jour du mot de passe : le module LDAP n'est pas installé" +msgstr "" +"Echec de la mise à jour du mot de passe : le module LDAP n'est pas installé" #: core/models.py:185 policyd/core.py:117 msgid "account" @@ -1632,7 +1791,9 @@ msgstr "L'utilisateur de base '{}' doit avoir une adresse email correcte" #: core/models.py:383 msgid "username and email fields must not differ for '{}'" -msgstr "les champs nom d'utilisateur et email ne doivent pas être différents pour '{}'" +msgstr "" +"les champs nom d'utilisateur et email ne doivent pas être différents pour " +"'{}'" #: core/password_validation.py:25 msgid "Password must contain at least {} digit." @@ -1664,7 +1825,9 @@ msgstr[2] "Le mot de passe doit contenir au moins {} caractères spéciaux." #: core/password_validation.py:70 msgid "Your password must contain a combination of different character types." -msgstr "Votre mot de passe doit contenir une combinaison de différents types de caractère." +msgstr "" +"Votre mot de passe doit contenir une combinaison de différents types de " +"caractère." #: core/sms_backends/ovh.py:21 core/sms_backends/ovh.py:102 msgid "API endpoint" @@ -1721,9 +1884,15 @@ msgid "" msgid_plural "" "\n" " The following updates are available:\n" -msgstr[0] "\nLa mise à jour suivante est disponible :\n" -msgstr[1] "\nLes mises à jour suivantes sont disponibles :\n" -msgstr[2] "\nLes mises à jour suivantes sont disponibles :\n" +msgstr[0] "" +"\n" +"La mise à jour suivante est disponible :\n" +msgstr[1] "" +"\n" +"Les mises à jour suivantes sont disponibles :\n" +msgstr[2] "" +"\n" +"Les mises à jour suivantes sont disponibles :\n" #: core/utils.py:43 msgid "The core part of Modoboa" @@ -1737,12 +1906,16 @@ msgstr "Utiliseur '%s' connecté avec succès" #: core/views/auth.py:109 #, python-format msgid "Password scheme mismatch. Updating %s password" -msgstr "La méthode de chiffrement du mot de passe diffère. Mise à jour du mot de passe de %s" +msgstr "" +"La méthode de chiffrement du mot de passe diffère. Mise à jour du mot de " +"passe de %s" #: core/views/auth.py:116 #, python-format msgid "Password hash parameter missmatch. Updating %s password" -msgstr "Le paramètre de hash du mot de passe ne correspond pas. Mise à jour %s du mot de passe" +msgstr "" +"Le paramètre de hash du mot de passe ne correspond pas. Mise à jour %s du " +"mot de passe" #: core/views/auth.py:154 msgid "User '{}' successfully logged out" @@ -1758,7 +1931,9 @@ msgstr "Activer les recherches inversées" #: dmarc/forms.py:23 msgid "Enable reverse DNS lookups (reports will be longer to display)" -msgstr "Activer les recherches DNS inversées (les rapports seront plus longs à afficher)" +msgstr "" +"Activer les recherches DNS inversées (les rapports seront plus longs à " +"afficher)" #: dmarc/forms.py:42 msgid "DMARC" @@ -1945,7 +2120,9 @@ msgstr "Filtre d'exclusion de dossier" msgid "" "Use a regular expression to explicitly include folders in sync. Example: " "^Trash$|Del" -msgstr "Utiliser une expression régulière pour explicitement inclure des dossiers à synchroniser. Exemple : ^Trash$|Del" +msgstr "" +"Utiliser une expression régulière pour explicitement inclure des dossiers à " +"synchroniser. Exemple : ^Trash$|Del" #: imap_migration/app_settings.py:74 msgid "Folder Filter Inclusions" @@ -1956,7 +2133,10 @@ msgid "" "A comma seperated list of folders to explicitly include in sync even if " "filtered by the Folder Filter Exclusions. Example: debian.user, " "debian.personal " -msgstr "Une liste de dossiers séparés par des virgules à explicitement inclure pendant la synchronisation même s'ils sont concernés par le filtre d'exclusion. Exemple debian.user, debian.personal" +msgstr "" +"Une liste de dossiers séparés par des virgules à explicitement inclure " +"pendant la synchronisation même s'ils sont concernés par le filtre " +"d'exclusion. Exemple debian.user, debian.personal" #: imap_migration/apps.py:20 msgid "IMAP Migration" @@ -1966,12 +2146,16 @@ msgstr "Migration IMAP" msgid "" "Automatic domain/mailbox creation is disabled which means IMAP " "authentication won't work." -msgstr "La création automatique de domaine/compte est désactivée, ce qui veut dire que l'authentification IMAP ne fonctionnera pas." +msgstr "" +"La création automatique de domaine/compte est désactivée, ce qui veut dire " +"que l'authentification IMAP ne fonctionnera pas." #: imap_migration/checks.py:16 msgid "" "Go to the online parameters panel (admin tab) and activate this feature." -msgstr "Rendez vous sur le panneau de configuration des paramètres (onglet Administration) et activez cette fonctionnalité." +msgstr "" +"Rendez vous sur le panneau de configuration des paramètres (onglet " +"Administration) et activez cette fonctionnalité." #: ldapsync/lib.py:82 msgid "Failed to create LDAP account: {}" @@ -2040,13 +2224,17 @@ msgstr "Admins de domaine" msgid "" "Maximum number of allowed domain administrators for a new administrator. (0 " "to deny any creation, -1 to allow unlimited creations)" -msgstr "Nombre maximal d'admins de domaine pour un nouvel administrateur. (0 pour interdire toute création, -1 pour autoriser les création illimitées)" +msgstr "" +"Nombre maximal d'admins de domaine pour un nouvel administrateur. (0 pour " +"interdire toute création, -1 pour autoriser les création illimitées)" #: limits/app_settings.py:41 msgid "" "Maximum number of allowed domains for a new administrator. (0 to deny any " "creation, -1 to allow unlimited creations)" -msgstr "Nombre maximal de domaines pour un nouvel administrateur. (0 pour interdire toute création, -1 pour autoriser les créations illimitées)" +msgstr "" +"Nombre maximal de domaines pour un nouvel administrateur. (0 pour interdire " +"toute création, -1 pour autoriser les créations illimitées)" #: limits/app_settings.py:49 limits/app_settings.py:123 limits/constants.py:22 #: limits/constants.py:76 @@ -2057,13 +2245,17 @@ msgstr "Alias de domaine" msgid "" "Maximum number of allowed domain aliases for a new administrator. (0 to deny" " any creation, -1 to allow unlimited creations)" -msgstr "Nombre maximal d'alias de domaine pour un nouvel administrateur. (0 pour interdire toute création, -1 pour autoriser les créations illimitées)" +msgstr "" +"Nombre maximal d'alias de domaine pour un nouvel administrateur. (0 pour " +"interdire toute création, -1 pour autoriser les créations illimitées)" #: limits/app_settings.py:64 msgid "" "Maximum number of allowed mailboxes for a new administrator. (0 to deny any " "creation, -1 to allow unlimited creations)" -msgstr "Nombre maximal de boites pour un nouvel administrateur. (0 pour interdire toute création, -1 pour autoriser les créations illimitées)" +msgstr "" +"Nombre maximal de boites pour un nouvel administrateur. (0 pour interdire " +"toute création, -1 pour autoriser les créations illimitées)" #: limits/app_settings.py:73 limits/app_settings.py:146 limits/constants.py:39 #: limits/constants.py:92 @@ -2074,7 +2266,9 @@ msgstr "Alias de boite" msgid "" "Maximum number of allowed aliases for a new administrator. (0 to deny any " "creation, -1 to allow unlimited creations)" -msgstr "Nombre maximal d'alias de boite pour un nouvel administrateur. (0 pour interdire toute création, -1 pour autoriser les créations illimitées)" +msgstr "" +"Nombre maximal d'alias de boite pour un nouvel administrateur. (0 pour " +"interdire toute création, -1 pour autoriser les créations illimitées)" #: limits/app_settings.py:84 limits/constants.py:59 msgid "Quota" @@ -2084,7 +2278,9 @@ msgstr "Quota" msgid "" "The quota a reseller will be allowed to share between the domains he " "creates. (0 means no quota)" -msgstr "Le quota qu'un revendeur sera autorisé à partager entre les domaines qu'il créé. (0 signifie pas de quota)" +msgstr "" +"Le quota qu'un revendeur sera autorisé à partager entre les domaines qu'il " +"créé. (0 signifie pas de quota)" #: limits/app_settings.py:98 msgid "Default per-domain limits" @@ -2102,25 +2298,34 @@ msgstr "Activer ou désactiver les limites par domaine" msgid "" "Maximum number of allowed domain administrators for a new domain. (0 to deny" " any creation, -1 to allow unlimited creations)" -msgstr "Nombre maximal d'administrateurs de domaine autorisés pour un nouveau domaine (0 pour interdire toute création, -1 pour autoriser les créations illimitées)" +msgstr "" +"Nombre maximal d'administrateurs de domaine autorisés pour un nouveau " +"domaine (0 pour interdire toute création, -1 pour autoriser les créations " +"illimitées)" #: limits/app_settings.py:126 msgid "" "Maximum number of allowed domain aliases for a new domain. (0 to deny any " "creation, -1 to allow unlimited creations)" -msgstr "Nombre maximal d'alias de domaine autorisés pour un nouveau domaine. (0 pour interdire toute création, -1 pour autoriser les créations illimitées)" +msgstr "" +"Nombre maximal d'alias de domaine autorisés pour un nouveau domaine. (0 pour" +" interdire toute création, -1 pour autoriser les créations illimitées)" #: limits/app_settings.py:138 msgid "" "Maximum number of allowed mailboxes for a new domain. (0 to deny any " "creation, -1 to allow unlimited creations)" -msgstr "Nombre maximal de boites aux lettres autorisées pour un nouveau domaine. (0 pour interdire toute création, -1 pour autoriser les créations illimitées)" +msgstr "" +"Nombre maximal de boites aux lettres autorisées pour un nouveau domaine. (0 " +"pour interdire toute création, -1 pour autoriser les créations illimitées)" #: limits/app_settings.py:149 msgid "" "Maximum number of allowed aliases for a new domain. (0 to deny any creation," " -1 to allow unlimited creations)" -msgstr "Nombre maximal d'alias autorisés pour un nouveau domaine. (0 pour interdire toute création, -1 pour autoriser les créations illimitées)" +msgstr "" +"Nombre maximal d'alias autorisés pour un nouveau domaine. (0 pour interdire " +"toute création, -1 pour autoriser les créations illimitées)" #: limits/apps.py:16 msgid "Limits" @@ -2164,7 +2369,8 @@ msgstr "Nombre maximal d'alias autorisés pour ce domaine." #: limits/constants.py:102 msgid "Maximum number of domain admins allowed for this domain." -msgstr "Nombre maximal d'administrateurs de domaine autorisés pour ce domaine." +msgstr "" +"Nombre maximal d'administrateurs de domaine autorisés pour ce domaine." #: limits/lib.py:19 #, python-format @@ -2185,7 +2391,9 @@ msgstr "illimité" #: maillog/api/v2/serializers.py:35 msgid "You must provide start and end dates when period is custom" -msgstr "Vous devez fournir des dates de début et fin quand la période est personnalisée" +msgstr "" +"Vous devez fournir des dates de début et fin quand la période est " +"personnalisée" #: maillog/apps.py:20 msgid "Path to the log file" @@ -2297,7 +2505,9 @@ msgstr "Activer les identifiants PDF" #: pdfcredentials/app_settings.py:21 msgid "Enabled PDF credentials document generation on account creation." -msgstr "Activer la génération d'un document PDF d'identifiants à la création d'un compte." +msgstr "" +"Activer la génération d'un document PDF d'identifiants à la création d'un " +"compte." #: pdfcredentials/app_settings.py:32 msgid "Documents storage" @@ -2323,7 +2533,9 @@ msgstr "Supprimer les documents après le premier téléchargement" msgid "" "Automatically delete a document just after its first download from this " "interface" -msgstr "Supprimer automatiquement un document juste après son premier téléchargement depuis cette interface" +msgstr "" +"Supprimer automatiquement un document juste après son premier téléchargement" +" depuis cette interface" #: pdfcredentials/app_settings.py:68 msgid "Generate documents only at account creation" @@ -2333,7 +2545,10 @@ msgstr "Générer les documents à la création des comptes uniquement" msgid "" "Generate a new document only when a new account is created. If set to no, a " "new document will be created each time a password is updated." -msgstr "Génère un nouveau document uniquement lors de la création d'un compte. Si défini à non, un nouveau document sera généré chaque fois qu'un mot de passe est changé." +msgstr "" +"Génère un nouveau document uniquement lors de la création d'un compte. Si " +"défini à non, un nouveau document sera généré chaque fois qu'un mot de passe" +" est changé." #: pdfcredentials/app_settings.py:84 msgid "Customization options" @@ -2371,7 +2586,9 @@ msgstr "Inclure les paramètres de connexion pour client mail" msgid "" "Include required SMTP and IMAP connection information to configure a mail " "client, a tablet or a phone" -msgstr "Inclure les paramètres de connexion nécessaires pour configurer un client mail, une tablette ou un téléphone" +msgstr "" +"Inclure les paramètres de connexion nécessaires pour configurer un client " +"mail, une tablette ou un téléphone" #: pdfcredentials/app_settings.py:123 pdfcredentials/documents.py:133 msgid "SMTP server address" @@ -2432,7 +2649,11 @@ msgid "" "Dear %s, this document contains the credentials you will need\n" "to connect to Modoboa. Learn the content and destroy\n" "the document as soon as possible.\n" -msgstr "\nCher %s, ce document contient les identifiants dont vous aurez\nbesoin pour vous connecter à Modoboa. Apprenez les puis détruisez\nce document le plus rapidement possible.\n" +msgstr "" +"\n" +"Cher %s, ce document contient les identifiants dont vous aurez\n" +"besoin pour vous connecter à Modoboa. Apprenez les puis détruisez\n" +"ce document le plus rapidement possible.\n" #: pdfcredentials/documents.py:95 msgid "Web panel:" @@ -2451,7 +2672,9 @@ msgid "" "\n" "Here you can view your emails anytime online, create filters or manage\n" "your contacts.\n" -msgstr "\nD'ici vous pouvez consulter vos emails en ligne n'importe quand, créer des filtres ou gérer vos contacts.\n" +msgstr "" +"\n" +"D'ici vous pouvez consulter vos emails en ligne n'importe quand, créer des filtres ou gérer vos contacts.\n" #: pdfcredentials/documents.py:126 msgid "Please change your password!" @@ -2463,13 +2686,17 @@ msgstr "Configuration PC/Tablette/Smartphone :" #: pdfcredentials/documents.py:153 msgid "Use those settings for your computer, tablet or phone." -msgstr "Utilisez ces paramètres pour votre ordinateur, votre tablette ou votre téléphone." +msgstr "" +"Utilisez ces paramètres pour votre ordinateur, votre tablette ou votre " +"téléphone." #: pdfcredentials/handlers.py:35 msgid "" "Failed to create PDF_credentials directory. Please check the permissions or " "the path." -msgstr "Echec lors de la création du répertoire PDF_credentials. Veuillez vérifier les permissions et le chemin d'accès." +msgstr "" +"Echec lors de la création du répertoire PDF_credentials. Veuillez vérifier " +"les permissions et le chemin d'accès." #: pdfcredentials/handlers.py:64 msgid "Download PDF credentials" @@ -2477,18 +2704,24 @@ msgstr "Télécharger les identifiants PDF" #: pdfcredentials/lib.py:29 msgid "Can't write to selected directory to store PDF documents" -msgstr "Impossible d'écrire dans le répertoire sélectionné pour stocker les documents PDF" +msgstr "" +"Impossible d'écrire dans le répertoire sélectionné pour stocker les " +"documents PDF" #: pdfcredentials/lib.py:35 #, python-format msgid "Failed to create the directory that will contain PDF documents (%s)" -msgstr "Echec lors de la création du répertoire qui contiendra les documents PDF (%s)" +msgstr "" +"Echec lors de la création du répertoire qui contiendra les documents PDF " +"(%s)" #: pdfcredentials/lib.py:82 msgid "" "Failed to create PDF_credentials file. Please check the permissions or the " "path." -msgstr "Echec lors de la création du fichier PDF_credentials. Veuillez vérifier les permissions et le chemin d'accès." +msgstr "" +"Echec lors de la création du fichier PDF_credentials. Veuillez vérifier les " +"permissions et le chemin d'accès." #: policyd/core.py:99 msgid "Daily sending limit reached" @@ -2505,7 +2738,8 @@ msgstr "[modoboa] Limite d'envoi atteinte" #: policyd/templates/policyd/notifications/limit_reached.html:3 #, python-format msgid "Daily limit of the following %(ltype)s has been reached: %(name)s." -msgstr "Limite quotidienne d'envoi du %(ltype)s suivant a été atteinte : %(name)s." +msgstr "" +"Limite quotidienne d'envoi du %(ltype)s suivant a été atteinte : %(name)s." #: relaydomains/api/v1/serializers.py:24 msgid "Unsupported service" @@ -2547,7 +2781,10 @@ msgstr "Chemin d'accès vers le fichier des clés" msgid "" "Absolute path of the file which contains paths to DKIM private keys. Must be" " readable by _rspamd group or user." -msgstr "Chemin absolu vers le fichier qui contient les chemins vers les clés privées DKIM. Doit être accessible en lecture par l'utilisateur ou le groupe _rspamd." +msgstr "" +"Chemin absolu vers le fichier qui contient les chemins vers les clés privées" +" DKIM. Doit être accessible en lecture par l'utilisateur ou le groupe " +"_rspamd." #: rspamd/app_settings.py:30 msgid "Selector map path" @@ -2557,7 +2794,9 @@ msgstr "Chemin d'accès vers le fichier des sélecteurs" msgid "" "Absolute path of the file which contains names of DKIM selectors. Must be " "readable by _rspamd group or user." -msgstr "Chemin absolu vers le fichier qui contient le nom des sélecteurs DKIM. Doit être accessible en lecture par l'utilisateur ou le groupe _rspamd." +msgstr "" +"Chemin absolu vers le fichier qui contient le nom des sélecteurs DKIM. Doit " +"être accessible en lecture par l'utilisateur ou le groupe _rspamd." #: rspamd/app_settings.py:51 msgid "Rspamd dashboard location" @@ -2568,7 +2807,9 @@ msgid "" "Location of the rspamd dashboard. Can either be a relative URL (eg. " "'/rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " "guidance on the Super-Admin dashboard." -msgstr "Emplacement du dashboard rspamd. Peut être soit une URL relative (ie. '/rspamd') ou une URL complète (ie. 'https://rspamd.domain.tld')." +msgstr "" +"Emplacement du dashboard rspamd. Peut être soit une URL relative (ie. " +"'/rspamd') ou une URL complète (ie. 'https://rspamd.domain.tld')." #: rspamd/apps.py:18 msgid "Rspamd" @@ -2693,7 +2934,8 @@ msgstr "Arrêter le traitement" #: sievefilters/lib.py:49 msgid "Connection to MANAGESIEVE server failed, check your configuration" -msgstr "La connexion au serveur MANAGESIEVE a échoué, vérifiez votre configuration" +msgstr "" +"La connexion au serveur MANAGESIEVE a échoué, vérifiez votre configuration" #: sievefilters/lib.py:76 msgid "Not enough space on server" @@ -2748,7 +2990,9 @@ msgstr "Mot de passe oublié ?" msgid "" "Your password has been set. You may go ahead and sign " "in now." -msgstr "Votre mot de passe a été défini. Vous pouvez continuer et vous connecter dès maintenant." +msgstr "" +"Votre mot de passe a été défini. Vous pouvez continuer et vous connecter dès maintenant." #: templates/registration/password_reset_confirm.html:8 msgid "Change password" @@ -2764,7 +3008,10 @@ msgstr "Soumettre" msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." -msgstr "Le lien de réinitialisation du mot de passe était invalide, probablement parce qu'il a déjà été utilisé. Merci de demander une nouvelle réinitialisation de mot de passe." +msgstr "" +"Le lien de réinitialisation du mot de passe était invalide, probablement " +"parce qu'il a déjà été utilisé. Merci de demander une nouvelle " +"réinitialisation de mot de passe." #: templates/registration/password_reset_confirm_code.html:22 msgid "Enter the code you've just received by SMS" @@ -2784,7 +3031,12 @@ msgid "" "exists with the email you entered. You should receive them shortly.

" "

If you don't receive an email, please make sure you've entered your " "primary address, and check your spam folder.

" -msgstr "

Nous vous avons envoyé par email les instructions pour définir votre mot de passe, pour peu qu'un compte avec l'email que vous avez renseigné existe. Vous devriez les recevoir rapidement.

Si vous ne recevez pas d'email, merci de vérifier que vous avez renseigné votre adresse principale et de vérifier votre dossier spam.

" +msgstr "" +"

Nous vous avons envoyé par email les instructions pour définir votre mot " +"de passe, pour peu qu'un compte avec l'email que vous avez renseigné existe." +" Vous devriez les recevoir rapidement.

Si vous ne recevez pas d'email," +" merci de vérifier que vous avez renseigné votre adresse principale et de " +"vérifier votre dossier spam.

" #: templates/registration/password_reset_email.html:4 #: templates/registration/password_reset_email_v2.html:4 @@ -2792,14 +3044,18 @@ msgstr "

Nous vous avons envoyé par email les instructions pour définir votr msgid "" "To initiate the password reset process for your %(username)s Modoboa " "account, click the link below:" -msgstr "Pour initier la procédure de réinitialisation du mot de passe de votre compte Modoboa %(username)s, cliquez sur le lien ci-dessous :" +msgstr "" +"Pour initier la procédure de réinitialisation du mot de passe de votre " +"compte Modoboa %(username)s, cliquez sur le lien ci-dessous :" #: templates/registration/password_reset_email.html:10 #: templates/registration/password_reset_email_v2.html:10 msgid "" "If clicking the link above doesn't work, please copy and paste the URL in a " "new browser window instead." -msgstr "Si cliquer sur lien ci-dessous ne fonctionne pas, merci de copier / coller cette URL dans une nouvelle fenêtre navigateur à la place." +msgstr "" +"Si cliquer sur lien ci-dessous ne fonctionne pas, merci de copier / coller " +"cette URL dans une nouvelle fenêtre navigateur à la place." #: templates/registration/password_reset_email.html:14 #: templates/registration/password_reset_email_v2.html:14 @@ -2830,7 +3086,10 @@ msgstr "Mot de passe jetable" msgid "" "Enter the code from the two-factor app on your mobile device. If you've lost" " your device, you may enter one of your recovery codes." -msgstr "Renseignez le code provenant de l'application double-facteur de votre téléphone mobile. Si vous avez perdu votre téléphone, vous pouvez utiliser l'un de vos codes de récupération." +msgstr "" +"Renseignez le code provenant de l'application double-facteur de votre " +"téléphone mobile. Si vous avez perdu votre téléphone, vous pouvez utiliser " +"l'un de vos codes de récupération." #: templates/registration/twofactor_code_verify.html:42 msgid "Verify code" @@ -2867,7 +3126,8 @@ msgstr "Taille maximale d'une pièce jointe" #: webmail/app_settings.py:19 msgid "Maximum attachment size in bytes (or KB, MB, GB if specified)" -msgstr "Taille maximale d'une pièce jointe en octets (ou KB, MG, GB si précisé)" +msgstr "" +"Taille maximale d'une pièce jointe en octets (ou KB, MG, GB si précisé)" #: webmail/app_settings.py:67 msgid "SMTP settings" @@ -2899,7 +3159,7 @@ msgstr "Le serveur nécessite une authentification" #: webmail/app_settings.py:118 msgid "Display" -msgstr "Affichage" +msgstr "Afficher" #: webmail/app_settings.py:124 msgid "Default message display mode" @@ -2919,7 +3179,7 @@ msgstr "Activer/Désactiver l'affichage des liens HTML" #: webmail/app_settings.py:144 msgid "Number of displayed emails per page" -msgstr "Nombre de messages affichés par page" +msgstr "Nombre d'emails affichés par page" #: webmail/app_settings.py:147 msgid "Sets the maximum number of messages displayed in a page" diff --git a/modoboa/locale/it/LC_MESSAGES/django.po b/modoboa/locale/it/LC_MESSAGES/django.po index c3162691d..23ca9ea07 100644 --- a/modoboa/locale/it/LC_MESSAGES/django.po +++ b/modoboa/locale/it/LC_MESSAGES/django.po @@ -14,8 +14,7 @@ msgstr "" "POT-Creation-Date: 2025-08-28 09:41+0200\n" "PO-Revision-Date: 2018-11-01 16:31+0000\n" "Last-Translator: marcotrevisan \n" -"Language-Team: Italian (http://www.transifex.com/tonio/modoboa/language/" -"it/)\n" +"Language-Team: Italian (http://www.transifex.com/tonio/modoboa/language/it/)\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -192,8 +191,8 @@ msgstr "MX validi" #: admin/app_settings.py:37 msgid "" -"A list of IP or network address every MX record should match. A warning will " -"be sent if a record does not respect it." +"A list of IP or network address every MX record should match. A warning will" +" be sent if a record does not respect it." msgstr "" "Una lista di indirizzi IP o host a cui ciascun record MX dovrebbe " "appartenere. Verrà inviato un avviso se un record non la rispetta. " @@ -295,8 +294,8 @@ msgid "" "world." msgstr "" "Percorso assoluto della cartella dove le chiavi private DKIM verranno " -"salvate. Assicurarsi che questa cartella sia di proprietà dell'utente root e " -"che non sia leggibile da altri." +"salvate. Assicurarsi che questa cartella sia di proprietà dell'utente root e" +" che non sia leggibile da altri." #: admin/app_settings.py:137 msgid "Default DKIM key length" @@ -389,7 +388,8 @@ msgid "Automatic domain/mailbox creation" msgstr "Creazione automatica del domini/casella di posta elettronica" #: admin/app_settings.py:221 -msgid "Create a domain and a mailbox when an account is automatically created." +msgid "" +"Create a domain and a mailbox when an account is automatically created." msgstr "" "Crea un dominio ed una casella di posta elettronica quando si crea un " "account utente" @@ -472,8 +472,8 @@ msgstr "Nessun DNS server funzionante trovato" #, python-format msgid "DNS resolution timeout, unable to query %s at the moment" msgstr "" -"Timeout nella risoluzione DNS del nome, problema momentaneo nell'interrogare " -"%s " +"Timeout nella risoluzione DNS del nome, problema momentaneo nell'interrogare" +" %s " #: admin/lib.py:236 msgid "DNS name is too long: {}" @@ -788,7 +788,8 @@ msgstr "Generale" #: autoreply/app_settings.py:18 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "Period between two auto-replies" msgstr "" "Tempo massimo in secondi tra due risposte automatiche al medesimo " @@ -796,7 +797,8 @@ msgstr "" #: autoreply/app_settings.py:20 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "" "Number of days to wait before sending a new auto-reply to the same recipient" msgstr "" @@ -1036,7 +1038,8 @@ msgstr "Numero di cicli non valido" msgid "This code is invalid" msgstr "" -#: core/api/v2/serializers.py:503 core/views/auth.py:194 core/views/auth.py:247 +#: core/api/v2/serializers.py:503 core/views/auth.py:194 +#: core/views/auth.py:247 msgid "Please use the following code to recover your Modoboa password: {}" msgstr "" @@ -1251,8 +1254,8 @@ msgstr "Gruppi degli amministratori" #: core/app_settings.py:239 msgid "" -"Members of those LDAP Posix groups will be created as domain administrators. " -"Use ';' characters to separate groups." +"Members of those LDAP Posix groups will be created as domain administrators." +" Use ';' characters to separate groups." msgstr "" "Gli appartenenti a questi gruppi LDAP Posix avranno i privilegi di " "amministratori di dominio. Utilizza il carattere ';' per separare valori " @@ -1461,13 +1464,15 @@ msgstr "Mostra RSS feed custom a rivenditori e amministratori di dominio" #: core/app_settings.py:506 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed to super admins" msgstr "Mostra RSS feed custom a rivenditori e amministratori di dominio" #: core/app_settings.py:509 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed also to super administrators" msgstr "Mostra RSS feed custom a rivenditori e amministratori di dominio" @@ -1587,8 +1592,8 @@ msgstr "Soglia account inattivo" #: core/app_settings.py:686 msgid "" -"Allow the administrator to set a threshold (in days) beyond which an account " -"is considered inactive if the user hasn't logged in" +"Allow the administrator to set a threshold (in days) beyond which an account" +" is considered inactive if the user hasn't logged in" msgstr "" #: core/app_settings.py:695 @@ -1597,8 +1602,8 @@ msgstr "Soglia account inattivo" #: core/app_settings.py:698 msgid "" -"An account with a last login date greater than this threshold (in days) will " -"be considered as inactive" +"An account with a last login date greater than this threshold (in days) will" +" be considered as inactive" msgstr "" "Un account con una data di ultimo accesso più vecchia di questa soglia (in " "giorni) verrà considerato inattivo" @@ -1674,8 +1679,9 @@ msgstr "" #: core/checks.py:34 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"dovecot.html#dovecot-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-" +"oauth2" msgstr "" #: core/checks.py:41 @@ -1686,8 +1692,9 @@ msgstr "" #: core/checks.py:45 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"radicale.html#radicale-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-" +"oauth2" msgstr "" #: core/constants.py:6 @@ -1877,7 +1884,8 @@ msgstr[1] "La password deve contenere almeno {} caratteri speciali." #: core/password_validation.py:70 msgid "Your password must contain a combination of different character types." msgstr "" -"La tua password deve contenere una combinazione di diversi tipi di carattere." +"La tua password deve contenere una combinazione di diversi tipi di " +"carattere." #: core/sms_backends/ovh.py:21 core/sms_backends/ovh.py:102 msgid "API endpoint" @@ -1997,7 +2005,7 @@ msgstr "Nessuno" #: dmarc/models.py:11 msgid "Quarantine" -msgstr "Quarantina" +msgstr "Quarantena" #: dmarc/models.py:12 msgid "Reject" @@ -2286,8 +2294,8 @@ msgid "" "Maximum number of allowed domains for a new administrator. (0 to deny any " "creation, -1 to allow unlimited creations)" msgstr "" -"Il numero massimo di domini per nuovi amministratori. (0 per disabilitare la " -"creazione, -1 per permettere la creazione illimitata)" +"Il numero massimo di domini per nuovi amministratori. (0 per disabilitare la" +" creazione, -1 per permettere la creazione illimitata)" #: limits/app_settings.py:49 limits/app_settings.py:123 limits/constants.py:22 #: limits/constants.py:76 @@ -2296,8 +2304,8 @@ msgstr "Alias del dominio" #: limits/app_settings.py:52 msgid "" -"Maximum number of allowed domain aliases for a new administrator. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain aliases for a new administrator. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Il numero massimo di alias per nuovi amministratori. (0 per disabilitare la " "creazione, -1 per permettere la creazione illimitata)" @@ -2349,11 +2357,11 @@ msgstr "Abilita o disabilita i limiti per dominio" #: limits/app_settings.py:114 msgid "" -"Maximum number of allowed domain administrators for a new domain. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain administrators for a new domain. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" -"Numero massimo di amministratori di dominio consentiti per un nuovo dominio. " -"(0 per negare qualunque creazione, -1 per permetterne senza limiti)" +"Numero massimo di amministratori di dominio consentiti per un nuovo dominio." +" (0 per negare qualunque creazione, -1 per permetterne senza limiti)" #: limits/app_settings.py:126 msgid "" @@ -2373,8 +2381,8 @@ msgstr "" #: limits/app_settings.py:149 msgid "" -"Maximum number of allowed aliases for a new domain. (0 to deny any creation, " -"-1 to allow unlimited creations)" +"Maximum number of allowed aliases for a new domain. (0 to deny any creation," +" -1 to allow unlimited creations)" msgstr "" "Numero massimo di alias consentiti per un nuovo dominio. (0 per negare " "qualunque creazione, -1 per permetterne senza limiti)" @@ -2614,8 +2622,8 @@ msgid "" "new document will be created each time a password is updated." msgstr "" "Generare un nuovo documento solo quando si crea un nuovo account. Se " -"impostato su no , un nuovo documento verrà creato ogni volta che la password " -"viene aggiornata." +"impostato su no , un nuovo documento verrà creato ogni volta che la password" +" viene aggiornata." #: pdfcredentials/app_settings.py:84 msgid "Customization options" @@ -2654,8 +2662,8 @@ msgid "" "Include required SMTP and IMAP connection information to configure a mail " "client, a tablet or a phone" msgstr "" -"Includi le informazioni di connessione SMTP e IMAP richieste per configurare " -"un client di posta, un tablet o telefono" +"Includi le informazioni di connessione SMTP e IMAP richieste per configurare" +" un client di posta, un tablet o telefono" #: pdfcredentials/app_settings.py:123 pdfcredentials/documents.py:133 msgid "SMTP server address" @@ -2772,7 +2780,8 @@ msgstr "Credenziali PDF" #: pdfcredentials/lib.py:29 #, fuzzy -#| msgid "Failed to create the directory that will contain PDF documents (%s)" +#| msgid "" +#| "Failed to create the directory that will contain PDF documents (%s)" msgid "Can't write to selected directory to store PDF documents" msgstr "Impossibile creare la cartella che conterrà i documenti PDF (%s)" @@ -2850,8 +2859,8 @@ msgstr "" #: rspamd/app_settings.py:21 msgid "" -"Absolute path of the file which contains paths to DKIM private keys. Must be " -"readable by _rspamd group or user." +"Absolute path of the file which contains paths to DKIM private keys. Must be" +" readable by _rspamd group or user." msgstr "" #: rspamd/app_settings.py:30 @@ -2870,8 +2879,8 @@ msgstr "" #: rspamd/app_settings.py:53 msgid "" -"Location of the rspamd dashboard. Can either be a relative URL (eg. '/" -"rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " +"Location of the rspamd dashboard. Can either be a relative URL (eg. " +"'/rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " "guidance on the Super-Admin dashboard." msgstr "" @@ -3088,7 +3097,8 @@ msgid "" "Your password has been set. You may go ahead and sign " "in now." msgstr "" -"La tua password è stata impostata. Ora puoi accedere." +"La tua password è stata impostata. Ora puoi accedere." #: templates/registration/password_reset_confirm.html:8 msgid "Change password" @@ -3183,8 +3193,8 @@ msgstr "Vecchia password" #: templates/registration/twofactor_code_verify.html:35 msgid "" -"Enter the code from the two-factor app on your mobile device. If you've lost " -"your device, you may enter one of your recovery codes." +"Enter the code from the two-factor app on your mobile device. If you've lost" +" your device, you may enter one of your recovery codes." msgstr "" #: templates/registration/twofactor_code_verify.html:42 @@ -3283,7 +3293,8 @@ msgstr "Modalità di visualizzazione del messaggio predefinita" #| msgid "The default redirection used when no application is specified" msgid "The default mode used when displaying a message" msgstr "" -"La modalità di visualizzazione predefinita quando viene mostrato il messaggio" +"La modalità di visualizzazione predefinita quando viene mostrato il " +"messaggio" #: webmail/app_settings.py:133 msgid "Enable HTML links display" @@ -3445,6 +3456,5 @@ msgid "Attachment is too big (limit: %s)" msgstr "L'allegato è troppo grande (limite: %s)" #, fuzzy, python-brace-format -#~| msgid "Connection to IMAP server failed: %s" #~ msgid "Connection to IMAP server failed: {error}" #~ msgstr "Connessione al server IMAP fallita: %s" diff --git a/modoboa/locale/nl_NL/LC_MESSAGES/django.po b/modoboa/locale/nl_NL/LC_MESSAGES/django.po index 65f7daba6..0b487fa8d 100644 --- a/modoboa/locale/nl_NL/LC_MESSAGES/django.po +++ b/modoboa/locale/nl_NL/LC_MESSAGES/django.po @@ -22,8 +22,7 @@ msgstr "" "POT-Creation-Date: 2025-08-28 09:41+0200\n" "PO-Revision-Date: 2024-07-17 11:46+0200\n" "Last-Translator: J Rosier \n" -"Language-Team: Dutch (Netherlands) (http://www.transifex.com/tonio/modoboa/" -"language/nl_NL/)\n" +"Language-Team: Dutch (Netherlands) (http://www.transifex.com/tonio/modoboa/language/nl_NL/)\n" "Language: nl_NL\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -194,11 +193,11 @@ msgstr "Geldige MX'en" #: admin/app_settings.py:37 msgid "" -"A list of IP or network address every MX record should match. A warning will " -"be sent if a record does not respect it." +"A list of IP or network address every MX record should match. A warning will" +" be sent if a record does not respect it." msgstr "" -"Een lijst van IP-adressen waaraan alle MX-records moeten voldoen. Er zal een " -"waarschuwing getoond worden voor records die niet overeenkomen." +"Een lijst van IP-adressen waaraan alle MX-records moeten voldoen. Er zal een" +" waarschuwing getoond worden voor records die niet overeenkomen." #: admin/app_settings.py:47 msgid "New domains must use authorized MX(s)" @@ -372,9 +371,11 @@ msgid "Automatic domain/mailbox creation" msgstr "Domein en mailbox automatisch aanmaken" #: admin/app_settings.py:221 -msgid "Create a domain and a mailbox when an account is automatically created." +msgid "" +"Create a domain and a mailbox when an account is automatically created." msgstr "" -"Maak een domein en mailbox aan wanneer een account automatisch is aangemaakt." +"Maak een domein en mailbox aan wanneer een account automatisch is " +"aangemaakt." #: admin/app_settings.py:230 msgid "Create an alias when a mailbox is renamed" @@ -382,7 +383,8 @@ msgstr "Maak een alias aan als een mailbox hernoemd wordt." #: admin/app_settings.py:233 msgid "Create an alias using the old address when a mailbox is renamed." -msgstr "Maak een alias aan voor het oude adres als een mailbox hernoemd wordt." +msgstr "" +"Maak een alias aan voor het oude adres als een mailbox hernoemd wordt." #: admin/app_settings.py:287 core/api/v2/viewsets.py:174 msgid "Administration" @@ -698,8 +700,8 @@ msgid "" "coming from it are considered as spam. You should contact those providers " "and ask them to unlist detected IP address(es)." msgstr "" -"De reputatie van dit domein wordt hierdoor aangetast er bestaat een kans dat " -"e-mail van dit domein als spam wordt gezien. Je zou contact moeten opnemen " +"De reputatie van dit domein wordt hierdoor aangetast er bestaat een kans dat" +" e-mail van dit domein als spam wordt gezien. Je zou contact moeten opnemen " "met deze providers en hun vragen om deze IP adressen van de lijst te halen." #: admin/templates/admin/notifications/domain_invalid_mx.html:3 @@ -749,14 +751,16 @@ msgstr "Algemeen" #: autoreply/app_settings.py:18 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "Period between two auto-replies" msgstr "" "Minimale tijd in seconden tussen twee auto-antwoorden naar dezelfde afzender" #: autoreply/app_settings.py:20 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "" "Number of days to wait before sending a new auto-reply to the same recipient" msgstr "" @@ -770,8 +774,8 @@ msgstr "Standaard onderwerp" msgid "" "Default subject used when an auto-reply message is created automatically" msgstr "" -"Standaard onderwerp gebruikt wanneer een automatisch antwoord automatisch is " -"aangemaakt" +"Standaard onderwerp gebruikt wanneer een automatisch antwoord automatisch is" +" aangemaakt" #: autoreply/app_settings.py:38 #, python-format @@ -994,7 +998,8 @@ msgstr "Ongeldig nummer van rondes" msgid "This code is invalid" msgstr "" -#: core/api/v2/serializers.py:503 core/views/auth.py:194 core/views/auth.py:247 +#: core/api/v2/serializers.py:503 core/views/auth.py:194 +#: core/views/auth.py:247 msgid "Please use the following code to recover your Modoboa password: {}" msgstr "Gebruik de volgende code om het Modoboa wachtwoord te herstellen: {}" @@ -1202,8 +1207,8 @@ msgstr "Beheerdergroepen" #: core/app_settings.py:239 msgid "" -"Members of those LDAP Posix groups will be created as domain administrators. " -"Use ';' characters to separate groups." +"Members of those LDAP Posix groups will be created as domain administrators." +" Use ';' characters to separate groups." msgstr "" "Leden van deze LDAP POSIX-groepen zullen aangemaakt worden als " "domeinbeheerders. Gebruik ';' karakters om groepen te scheiden." @@ -1311,7 +1316,8 @@ msgstr "Activeer export naar LDAP" msgid "" "Enable automatic synchronization between local database and LDAP directory" msgstr "" -"Activeer automatische synchronisatie tussen lokale database en LDAP directory" +"Activeer automatische synchronisatie tussen lokale database en LDAP " +"directory" #: core/app_settings.py:389 msgid "Delete remote LDAP account when local account is deleted" @@ -1344,7 +1350,8 @@ msgstr "Activeer import van LDAP" #: core/app_settings.py:414 msgid "Enable account synchronization from LDAP directory to local database" -msgstr "Activeer accountsynchronisatie van LDAP directory naar lokale database" +msgstr "" +"Activeer accountsynchronisatie van LDAP directory naar lokale database" #: core/app_settings.py:444 msgid "Username attribute" @@ -1399,13 +1406,15 @@ msgstr "Toon aangepaste RSS feed aan resellers en domein beheerders. " #: core/app_settings.py:506 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed to super admins" msgstr "Toon aangepaste RSS feed aan resellers en domein beheerders. " #: core/app_settings.py:509 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed also to super administrators" msgstr "Toon aangepaste RSS feed aan resellers en domein beheerders. " @@ -1525,8 +1534,8 @@ msgstr "Drempelwaarde inactieve gebruikers" #: core/app_settings.py:686 msgid "" -"Allow the administrator to set a threshold (in days) beyond which an account " -"is considered inactive if the user hasn't logged in" +"Allow the administrator to set a threshold (in days) beyond which an account" +" is considered inactive if the user hasn't logged in" msgstr "" #: core/app_settings.py:695 @@ -1535,8 +1544,8 @@ msgstr "Drempelwaarde inactieve gebruikers" #: core/app_settings.py:698 msgid "" -"An account with a last login date greater than this threshold (in days) will " -"be considered as inactive" +"An account with a last login date greater than this threshold (in days) will" +" be considered as inactive" msgstr "" "Een gebruiker die langer dan dit aantal dagen niet ingelogd is, wordt op " "inactief gezet." @@ -1615,8 +1624,9 @@ msgstr "" #: core/checks.py:34 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"dovecot.html#dovecot-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-" +"oauth2" msgstr "" #: core/checks.py:41 @@ -1627,8 +1637,9 @@ msgstr "" #: core/checks.py:45 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"radicale.html#radicale-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-" +"oauth2" msgstr "" #: core/constants.py:6 @@ -2214,7 +2225,8 @@ msgid "" "Maximum number of allowed domain administrators for a new administrator. (0 " "to deny any creation, -1 to allow unlimited creations)" msgstr "" -"Maximum aantal toegestane domein administrators voor een nieuwe administrator" +"Maximum aantal toegestane domein administrators voor een nieuwe " +"administrator" #: limits/app_settings.py:41 #, fuzzy @@ -2231,10 +2243,11 @@ msgstr "Domeinaliassen" #: limits/app_settings.py:52 #, fuzzy -#| msgid "Maximum number of allowed domain aliases for a new administrator" +#| msgid "" +#| "Maximum number of allowed domain aliases for a new administrator" msgid "" -"Maximum number of allowed domain aliases for a new administrator. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain aliases for a new administrator. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "Maximum aantal domein aliassen voor een nieuwe administrator" #: limits/app_settings.py:64 @@ -2287,14 +2300,16 @@ msgstr "" #| msgid "" #| "Maximum number of allowed domain administrators for a new administrator" msgid "" -"Maximum number of allowed domain administrators for a new domain. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain administrators for a new domain. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" -"Maximum aantal toegestane domein administrators voor een nieuwe administrator" +"Maximum aantal toegestane domein administrators voor een nieuwe " +"administrator" #: limits/app_settings.py:126 #, fuzzy -#| msgid "Maximum number of allowed domain aliases for a new administrator" +#| msgid "" +#| "Maximum number of allowed domain aliases for a new administrator" msgid "" "Maximum number of allowed domain aliases for a new domain. (0 to deny any " "creation, -1 to allow unlimited creations)" @@ -2312,8 +2327,8 @@ msgstr "Maximum aantal toegestane mailboxen voor een nieuwe administrator" #, fuzzy #| msgid "Maximum number of allowed aliases for a new administrator" msgid "" -"Maximum number of allowed aliases for a new domain. (0 to deny any creation, " -"-1 to allow unlimited creations)" +"Maximum number of allowed aliases for a new domain. (0 to deny any creation," +" -1 to allow unlimited creations)" msgstr "Maximum aantal aliassen voor een nieuwe administrator" #: limits/apps.py:16 @@ -2553,8 +2568,8 @@ msgid "" "new document will be created each time a password is updated." msgstr "" "Genereer een document enkel wanneer een account is nieuw aangemaakt. Indien " -"'nee' is geselecteerd zal bij iedere wachtwoord wijziging een nieuw document " -"worden gemaakt. " +"'nee' is geselecteerd zal bij iedere wachtwoord wijziging een nieuw document" +" worden gemaakt. " #: pdfcredentials/app_settings.py:84 msgid "Customization options" @@ -2681,8 +2696,7 @@ msgid "" "your contacts.\n" msgstr "" "\n" -"Hier kunt u uw e-mails online bekijken, filters aanmaken, of contactpersonen " -"beheren.\n" +"Hier kunt u uw e-mails online bekijken, filters aanmaken, of contactpersonen beheren.\n" #: pdfcredentials/documents.py:126 msgid "Please change your password!" @@ -2710,7 +2724,8 @@ msgstr "PDF gegevens" #: pdfcredentials/lib.py:29 #, fuzzy -#| msgid "Failed to create the directory that will contain PDF documents (%s)" +#| msgid "" +#| "Failed to create the directory that will contain PDF documents (%s)" msgid "Can't write to selected directory to store PDF documents" msgstr "Het aanmaken van de map voor de PDF documenten is mislukt (%s)" @@ -2792,8 +2807,8 @@ msgstr "" #: rspamd/app_settings.py:21 msgid "" -"Absolute path of the file which contains paths to DKIM private keys. Must be " -"readable by _rspamd group or user." +"Absolute path of the file which contains paths to DKIM private keys. Must be" +" readable by _rspamd group or user." msgstr "" #: rspamd/app_settings.py:30 @@ -2812,8 +2827,8 @@ msgstr "" #: rspamd/app_settings.py:53 msgid "" -"Location of the rspamd dashboard. Can either be a relative URL (eg. '/" -"rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " +"Location of the rspamd dashboard. Can either be a relative URL (eg. " +"'/rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " "guidance on the Super-Admin dashboard." msgstr "" @@ -2969,7 +2984,8 @@ msgstr "" #, fuzzy #| msgid "Connection to IMAP server failed: %s" msgid "Connection to MANAGESIEVE server failed, check your configuration" -msgstr "Verbinding naar MANAGESIEVE server mislukt, controleer uw configuratie" +msgstr "" +"Verbinding naar MANAGESIEVE server mislukt, controleer uw configuratie" #: sievefilters/lib.py:76 #, fuzzy @@ -3028,8 +3044,7 @@ msgstr "Wachtwoord vergeten?" msgid "" "Your password has been set. You may go ahead and sign " "in now." -msgstr "" -"Je wachtwoord is ingesteld. Je kunt nu inloggen" +msgstr "Je wachtwoord is ingesteld. Je kunt nu inloggen" #: templates/registration/password_reset_confirm.html:8 msgid "Change password" @@ -3068,10 +3083,8 @@ msgid "" "

If you don't receive an email, please make sure you've entered your " "primary address, and check your spam folder.

" msgstr "" -"

We hebben je instructies gemailt om je wachtwoord in te stellen. Als het " -"door jou ingevulde email adres bestaat, zal je hem binnenkort ontvangen

\n" -"

Als je geen email ontvangt, controleer dan of je het juiste email adres " -"hebt ingevuld, en controleer je spam folder.

" +"

We hebben je instructies gemailt om je wachtwoord in te stellen. Als het door jou ingevulde email adres bestaat, zal je hem binnenkort ontvangen

\n" +"

Als je geen email ontvangt, controleer dan of je het juiste email adres hebt ingevuld, en controleer je spam folder.

" #: templates/registration/password_reset_email.html:4 #: templates/registration/password_reset_email_v2.html:4 @@ -3123,8 +3136,8 @@ msgstr "Oud wachtwoord" #: templates/registration/twofactor_code_verify.html:35 msgid "" -"Enter the code from the two-factor app on your mobile device. If you've lost " -"your device, you may enter one of your recovery codes." +"Enter the code from the two-factor app on your mobile device. If you've lost" +" your device, you may enter one of your recovery codes." msgstr "" #: templates/registration/twofactor_code_verify.html:42 @@ -3236,7 +3249,7 @@ msgstr "(De)activeer weergave van HTML-koppelingen" #, fuzzy #| msgid "Number of displayed items per page" msgid "Number of displayed emails per page" -msgstr "Aantal weergegeven e-mails per pagina" +msgstr "Aantal weergegeven emails per pagina" #: webmail/app_settings.py:147 msgid "Sets the maximum number of messages displayed in a page" @@ -3376,6 +3389,5 @@ msgid "Attachment is too big (limit: %s)" msgstr "Bijlage is te groot (maximaal toegestane grootte: %s)" #, fuzzy, python-brace-format -#~| msgid "Connection to IMAP server failed: %s" #~ msgid "Connection to IMAP server failed: {error}" #~ msgstr "Verbinding naar IMAP server mislukt: %s" diff --git a/modoboa/locale/pl_PL/LC_MESSAGES/django.po b/modoboa/locale/pl_PL/LC_MESSAGES/django.po index bfcd3d14a..835453765 100644 --- a/modoboa/locale/pl_PL/LC_MESSAGES/django.po +++ b/modoboa/locale/pl_PL/LC_MESSAGES/django.po @@ -18,23 +18,20 @@ msgstr "" "POT-Creation-Date: 2025-08-28 09:41+0200\n" "PO-Revision-Date: 2021-07-09 07:40+0000\n" "Last-Translator: KrystianPlichta \n" -"Language-Team: Polish (Poland) (http://www.transifex.com/tonio/modoboa/" -"language/pl_PL/)\n" +"Language-Team: Polish (Poland) (http://www.transifex.com/tonio/modoboa/language/pl_PL/)\n" "Language: pl_PL\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && " -"(n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && " -"n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" +"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" #: admin/api/v1/serializers.py:39 msgid "" "Quota shared between mailboxes. Can be expressed in KB, MB (default) or GB. " "A value of 0 means no quota." msgstr "" -"Miejsce dzielone między skrzynki. Może być wyrażone w KB, MB (domyślnie) lub " -"GB. Wartość 0 oznacza brak limitu." +"Miejsce dzielone między skrzynki. Może być wyrażone w KB, MB (domyślnie) lub" +" GB. Wartość 0 oznacza brak limitu." #: admin/api/v1/serializers.py:46 msgid "Default quota in MB applied to mailboxes. A value of 0 means no quota." @@ -196,11 +193,11 @@ msgstr "Poprawne MXy" #: admin/app_settings.py:37 msgid "" -"A list of IP or network address every MX record should match. A warning will " -"be sent if a record does not respect it." +"A list of IP or network address every MX record should match. A warning will" +" be sent if a record does not respect it." msgstr "" -"Lista adresów IP lub sieci dla każdego pasującego rekordu MX. Monit zostanie " -"wysłany w przypadku błędu." +"Lista adresów IP lub sieci dla każdego pasującego rekordu MX. Monit zostanie" +" wysłany w przypadku błędu." #: admin/app_settings.py:47 msgid "New domains must use authorized MX(s)" @@ -284,8 +281,8 @@ msgid "" "sure this directory belongs to root user and is not readable by the outside " "world." msgstr "" -"Ścieżka bezwzględna do katalogu w którym będą trzymane klucze prywatne DKIM. " -"Upewnij się że właścicielem katalogu jest \"użytkownik root\" i że katalog " +"Ścieżka bezwzględna do katalogu w którym będą trzymane klucze prywatne DKIM." +" Upewnij się że właścicielem katalogu jest \"użytkownik root\" i że katalog " "nie jest publicznie dostępny do odczytu." #: admin/app_settings.py:137 @@ -317,8 +314,8 @@ msgid "" "Number of messages freshly created domains will be allowed to send per day. " "Leave empty for no limit." msgstr "" -"Liczba wiadomości, które świeżo utworzone domeny będą mogły wysłać dziennie. " -"Pozostaw puste dla braku limitu." +"Liczba wiadomości, które świeżo utworzone domeny będą mogły wysłać dziennie." +" Pozostaw puste dla braku limitu." #: admin/app_settings.py:170 limits/app_settings.py:61 #: limits/app_settings.py:135 limits/constants.py:31 limits/constants.py:84 @@ -334,8 +331,8 @@ msgid "" "Rename or remove mailboxes on the filesystem when they get renamed or " "removed within Modoboa" msgstr "" -"Zmieniaj lub usuwaj skrzynki na filesystemie jeśli zostały zmodyfikowane lub " -"usuniętę przez Modoboa" +"Zmieniaj lub usuwaj skrzynki na filesystemie jeśli zostały zmodyfikowane lub" +" usuniętę przez Modoboa" #: admin/app_settings.py:186 admin/models/domain.py:36 msgid "Default mailbox quota" @@ -344,8 +341,8 @@ msgstr "Domyślna przestrzeń dla konta pocztowego" #: admin/app_settings.py:188 #, fuzzy #| msgid "" -#| "Default mailbox quota (in MB) applied to freshly created mailboxes with " -#| "no value specified. A value of 0 means no quota." +#| "Default mailbox quota (in MB) applied to freshly created mailboxes with no " +#| "value specified. A value of 0 means no quota." msgid "" "Default mailbox quota (in MB) applied to freshly created domains with no " "value specified. A value of 0 means no quota." @@ -378,7 +375,8 @@ msgid "Automatic domain/mailbox creation" msgstr "Automatyczne tworzenie domeny/skrzynki" #: admin/app_settings.py:221 -msgid "Create a domain and a mailbox when an account is automatically created." +msgid "" +"Create a domain and a mailbox when an account is automatically created." msgstr "Utwórz domenę i skrzynkę gdy tworzone jest konto" #: admin/app_settings.py:230 @@ -454,8 +452,8 @@ msgstr "Znaleziono nie działające serwery nazw" #, python-format msgid "DNS resolution timeout, unable to query %s at the moment" msgstr "" -"Upłynąć czas na rozwiązanie nazwy w DNS, w tym momencie nie potrafię odpytać " -"%s" +"Upłynąć czas na rozwiązanie nazwy w DNS, w tym momencie nie potrafię odpytać" +" %s" #: admin/lib.py:236 msgid "DNS name is too long: {}" @@ -618,8 +616,8 @@ msgstr "Włącz podpisywanie DKIM" msgid "" "If you activate this feature, a DKIM key will be generated for this domain." msgstr "" -"Jeśli włączysz tą funkcjonalność, dla tej domeny zostanie wygenerowany klucz " -"DKIM" +"Jeśli włączysz tą funkcjonalność, dla tej domeny zostanie wygenerowany klucz" +" DKIM" #: admin/models/domain.py:70 msgid "Key length" @@ -764,14 +762,16 @@ msgstr "Ogólne" #: autoreply/app_settings.py:18 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "Period between two auto-replies" msgstr "" "Limit w sek. pomiędzie dwiema auto-odpowiedziami dla tego samego odbiorcy" #: autoreply/app_settings.py:20 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "" "Number of days to wait before sending a new auto-reply to the same recipient" msgstr "" @@ -1006,7 +1006,8 @@ msgstr "Nieprawidłowa liczba rund" msgid "This code is invalid" msgstr "Ten kod jest nieprawidłowy" -#: core/api/v2/serializers.py:503 core/views/auth.py:194 core/views/auth.py:247 +#: core/api/v2/serializers.py:503 core/views/auth.py:194 +#: core/views/auth.py:247 msgid "Please use the following code to recover your Modoboa password: {}" msgstr "Proszę użyć następującego kodu, aby odzyskać hasło Modoboa: {}" @@ -1217,11 +1218,11 @@ msgstr "Administrator grup" #: core/app_settings.py:239 msgid "" -"Members of those LDAP Posix groups will be created as domain administrators. " -"Use ';' characters to separate groups." +"Members of those LDAP Posix groups will be created as domain administrators." +" Use ';' characters to separate groups." msgstr "" -"Członkowie tych grup LDAP Posix będą utworzeni jako administratorzy domeny. " -"Użyj znaku ';' jako separatora grup." +"Członkowie tych grup LDAP Posix będą utworzeni jako administratorzy domeny." +" Użyj znaku ';' jako separatora grup." #: core/app_settings.py:248 msgid "Group type" @@ -1321,7 +1322,8 @@ msgstr "Włącz export do LDAP" #: core/app_settings.py:380 msgid "" "Enable automatic synchronization between local database and LDAP directory" -msgstr "Włącz automatyczną synchronizację lokalnej bazy danych i katalogu LDAP" +msgstr "" +"Włącz automatyczną synchronizację lokalnej bazy danych i katalogu LDAP" #: core/app_settings.py:389 msgid "Delete remote LDAP account when local account is deleted" @@ -1332,8 +1334,8 @@ msgid "" "Delete remote LDAP account when local account is deleted, otherwise it will " "be disabled." msgstr "" -"Skasuj konto na zdalnym LDAP kiedy zostanie skasowane konto lokalne, w innym " -"przypadku zostanie ono wyłączone." +"Skasuj konto na zdalnym LDAP kiedy zostanie skasowane konto lokalne, w innym" +" przypadku zostanie ono wyłączone." #: core/app_settings.py:401 msgid "Account DN template" @@ -1345,8 +1347,8 @@ msgid "" "The template used to construct an account's DN. It should contain one " "placeholder (ie. %(user)s)" msgstr "" -"Szablon używany do budowy DN konta. Powinien zawierać jeden placeholder (ie. " -"%(user)s)" +"Szablon używany do budowy DN konta. Powinien zawierać jeden placeholder (ie." +" %(user)s)" #: core/app_settings.py:412 msgid "Enable import from LDAP" @@ -1371,7 +1373,8 @@ msgstr "Włącz synchronizację Dovecot LDAP" #: core/app_settings.py:457 msgid "LDAP authentication settings will be applied to Dovecot configuration." msgstr "" -"Ustawienia uwierzytelniania LDAP zostaną zastosowane do konfiguracji Dovecot." +"Ustawienia uwierzytelniania LDAP zostaną zastosowane do konfiguracji " +"Dovecot." #: core/app_settings.py:465 msgid "Dovecot LDAP config file" @@ -1412,14 +1415,16 @@ msgstr "" #: core/app_settings.py:506 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed to super admins" msgstr "" "Wyświetl spersonalizowany kanał RSS odsprzedawcy i administratorom domen" #: core/app_settings.py:509 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed also to super administrators" msgstr "" "Wyświetl spersonalizowany kanał RSS odsprzedawcy i administratorom domen" @@ -1529,7 +1534,8 @@ msgstr "Wyślij statystyki" #: core/app_settings.py:664 msgid "Send statistics to Modoboa public API (counters and used extensions)" msgstr "" -"Prześlij statystyki do publicznego API Modoboa (licznik i użyte rozszerzenia)" +"Prześlij statystyki do publicznego API Modoboa (licznik i użyte " +"rozszerzenia)" #: core/app_settings.py:683 #, fuzzy @@ -1539,8 +1545,8 @@ msgstr "Próg nieaktywności konta" #: core/app_settings.py:686 msgid "" -"Allow the administrator to set a threshold (in days) beyond which an account " -"is considered inactive if the user hasn't logged in" +"Allow the administrator to set a threshold (in days) beyond which an account" +" is considered inactive if the user hasn't logged in" msgstr "" #: core/app_settings.py:695 @@ -1549,8 +1555,8 @@ msgstr "Próg nieaktywności konta" #: core/app_settings.py:698 msgid "" -"An account with a last login date greater than this threshold (in days) will " -"be considered as inactive" +"An account with a last login date greater than this threshold (in days) will" +" be considered as inactive" msgstr "" "Konto z datą ostatniego logowania większą niż podany próg (w dniach) " "zostanie uznane za nieaktywne. " @@ -1627,8 +1633,9 @@ msgstr "" #: core/checks.py:34 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"dovecot.html#dovecot-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-" +"oauth2" msgstr "" #: core/checks.py:41 @@ -1639,8 +1646,9 @@ msgstr "" #: core/checks.py:45 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"radicale.html#radicale-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-" +"oauth2" msgstr "" #: core/constants.py:6 @@ -1956,7 +1964,7 @@ msgstr "Brak" #, fuzzy #| msgid "Quantity" msgid "Quarantine" -msgstr "Ilość" +msgstr "Kwarantanna" #: dmarc/models.py:12 #, fuzzy @@ -2239,16 +2247,16 @@ msgid "" "Maximum number of allowed domain administrators for a new administrator. (0 " "to deny any creation, -1 to allow unlimited creations)" msgstr "" -"Maksymalna dozwolona liczba administratorów domen dla nowego administratora. " -"(0 - tworzenie niedozwolone, -1 nielimitowana ilość)" +"Maksymalna dozwolona liczba administratorów domen dla nowego administratora." +" (0 - tworzenie niedozwolone, -1 nielimitowana ilość)" #: limits/app_settings.py:41 msgid "" "Maximum number of allowed domains for a new administrator. (0 to deny any " "creation, -1 to allow unlimited creations)" msgstr "" -"Maksymalna dozwolona ilość domen dla nowego administratora. (0 dla tworzenie " -"niedozwolone, -1 nielimitowana ilość)" +"Maksymalna dozwolona ilość domen dla nowego administratora. (0 dla tworzenie" +" niedozwolone, -1 nielimitowana ilość)" #: limits/app_settings.py:49 limits/app_settings.py:123 limits/constants.py:22 #: limits/constants.py:76 @@ -2257,8 +2265,8 @@ msgstr "Aliasy domen" #: limits/app_settings.py:52 msgid "" -"Maximum number of allowed domain aliases for a new administrator. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain aliases for a new administrator. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Maksymalna dozwolona ilość aliasów domen dla nowego administratora. (0 dla " "tworzenie niedozwolone, -1 nielimitowana ilość)" @@ -2310,8 +2318,8 @@ msgstr "Włącz lub wyłącz limity na domenę" #: limits/app_settings.py:114 msgid "" -"Maximum number of allowed domain administrators for a new domain. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain administrators for a new domain. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Max liczba administratrów nowej domeny. (0 tworzenie niedozwolone, -1 dla " "braku limitu)" @@ -2334,8 +2342,8 @@ msgstr "" #: limits/app_settings.py:149 msgid "" -"Maximum number of allowed aliases for a new domain. (0 to deny any creation, " -"-1 to allow unlimited creations)" +"Maximum number of allowed aliases for a new domain. (0 to deny any creation," +" -1 to allow unlimited creations)" msgstr "" "Max liczba aliasów dla nowej domeny. (0 tworzenie niedozwolone, -1 " "nielimitowana ilość)" @@ -2549,7 +2557,8 @@ msgid "" "Automatically delete a document just after its first download from this " "interface" msgstr "" -"Automatycznie usuwaj dokumenty jak tylko zostaną pobranie przez ten interfejs" +"Automatycznie usuwaj dokumenty jak tylko zostaną pobranie przez ten " +"interfejs" #: pdfcredentials/app_settings.py:68 msgid "Generate documents only at account creation" @@ -2561,7 +2570,8 @@ msgid "" "new document will be created each time a password is updated." msgstr "" "Generuj nowy dokument tylko wtedy, kiedy nowe konto jest zakładane. Jeśli " -"ustawione na nie, to nowy dokument będzie tworzony przy każdej zmianie hasła." +"ustawione na nie, to nowy dokument będzie tworzony przy każdej zmianie " +"hasła." #: pdfcredentials/app_settings.py:84 msgid "Customization options" @@ -2664,9 +2674,7 @@ msgid "" "the document as soon as possible.\n" msgstr "" "\n" -"Drogi użytkowniku %s, ten dokument zawiera dane uwierzytelniające które " -"będzeisz potrzebował to połączenia się. Zapoznaj się z nim i usuń go " -"najszybciej jak to możliwe.\n" +"Drogi użytkowniku %s, ten dokument zawiera dane uwierzytelniające które będzeisz potrzebował to połączenia się. Zapoznaj się z nim i usuń go najszybciej jak to możliwe.\n" #: pdfcredentials/documents.py:95 msgid "Web panel:" @@ -2687,8 +2695,7 @@ msgid "" "your contacts.\n" msgstr "" "\n" -"Pod tym adresem możesz online przeczytać swoje wiadomości, stworzyć filtry " -"lub zarządzać\n" +"Pod tym adresem możesz online przeczytać swoje wiadomości, stworzyć filtry lub zarządzać\n" " listą kontaktów.\n" #: pdfcredentials/documents.py:126 @@ -2717,7 +2724,8 @@ msgstr "Poświadczenia PDF" #: pdfcredentials/lib.py:29 #, fuzzy -#| msgid "Failed to create the directory that will contain PDF documents (%s)" +#| msgid "" +#| "Failed to create the directory that will contain PDF documents (%s)" msgid "Can't write to selected directory to store PDF documents" msgstr "Nie można utworzyć katalogu do przechowywania PDF'ów (%s)" @@ -2795,8 +2803,8 @@ msgstr "" #: rspamd/app_settings.py:21 msgid "" -"Absolute path of the file which contains paths to DKIM private keys. Must be " -"readable by _rspamd group or user." +"Absolute path of the file which contains paths to DKIM private keys. Must be" +" readable by _rspamd group or user." msgstr "" #: rspamd/app_settings.py:30 @@ -2815,8 +2823,8 @@ msgstr "" #: rspamd/app_settings.py:53 msgid "" -"Location of the rspamd dashboard. Can either be a relative URL (eg. '/" -"rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " +"Location of the rspamd dashboard. Can either be a relative URL (eg. " +"'/rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " "guidance on the Super-Admin dashboard." msgstr "" @@ -3129,8 +3137,8 @@ msgstr "Stare hasło" #: templates/registration/twofactor_code_verify.html:35 msgid "" -"Enter the code from the two-factor app on your mobile device. If you've lost " -"your device, you may enter one of your recovery codes." +"Enter the code from the two-factor app on your mobile device. If you've lost" +" your device, you may enter one of your recovery codes." msgstr "" #: templates/registration/twofactor_code_verify.html:42 @@ -3142,7 +3150,7 @@ msgstr "Kod weryfikacyjny" #: templates/registration/twofactor_code_verify.html:45 #: templates/registration/twofactor_code_verify.html:51 msgid "Back" -msgstr "" +msgstr "Wstecz" #: templates/registration/twofactor_code_verify.html:49 msgid "Log using Webauthn" @@ -3215,7 +3223,7 @@ msgstr "Preferowany mechanizm uwierzytelnienia" #: webmail/app_settings.py:118 msgid "Display" -msgstr "" +msgstr "Wyświetl" #: webmail/app_settings.py:124 msgid "Default message display mode" @@ -3239,7 +3247,7 @@ msgstr "" #, fuzzy #| msgid "Number of displayed items per page" msgid "Number of displayed emails per page" -msgstr "Liczba wyświetlanych elementów na stronie" +msgstr "Liczba wyświetlanych wiadomości na stronie" #: webmail/app_settings.py:147 msgid "Sets the maximum number of messages displayed in a page" @@ -3395,6 +3403,5 @@ msgid "Attachment is too big (limit: %s)" msgstr "" #, fuzzy, python-brace-format -#~| msgid "Connection to IMAP server failed: %s" #~ msgid "Connection to IMAP server failed: {error}" #~ msgstr "Błąd połączenia do serwera IMAP: %s" diff --git a/modoboa/locale/pt_BR/LC_MESSAGES/django.po b/modoboa/locale/pt_BR/LC_MESSAGES/django.po index 4373f3cb4..8138c22ce 100644 --- a/modoboa/locale/pt_BR/LC_MESSAGES/django.po +++ b/modoboa/locale/pt_BR/LC_MESSAGES/django.po @@ -28,16 +28,13 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-08-28 09:41+0200\n" "PO-Revision-Date: 2011-07-01 08:15+0000\n" -"Last-Translator: Rafael Barretto Alves , " -"2013,2015-2017,2025\n" -"Language-Team: Portuguese (Brazil) (http://app.transifex.com/tonio/modoboa/" -"language/pt_BR/)\n" +"Last-Translator: Rafael Barretto Alves , 2013,2015-2017,2025\n" +"Language-Team: Portuguese (Brazil) (http://app.transifex.com/tonio/modoboa/language/pt_BR/)\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % " -"1000000 == 0 ? 1 : 2;\n" +"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" #: admin/api/v1/serializers.py:39 msgid "" @@ -205,11 +202,11 @@ msgstr "MXs válidos" #: admin/app_settings.py:37 msgid "" -"A list of IP or network address every MX record should match. A warning will " -"be sent if a record does not respect it." +"A list of IP or network address every MX record should match. A warning will" +" be sent if a record does not respect it." msgstr "" -"Uma lista de endereços IP ou de rede que cada registro MX deve corresponder. " -"Um aviso será enviado se um registro não for respeitado." +"Uma lista de endereços IP ou de rede que cada registro MX deve corresponder." +" Um aviso será enviado se um registro não for respeitado." #: admin/app_settings.py:47 msgid "New domains must use authorized MX(s)" @@ -246,8 +243,8 @@ msgstr "Ativar verificações DKIM" #: admin/app_settings.py:80 msgid "Check if every domain with DKIM signin enabled has a valid DNS record" msgstr "" -"Verifique se todos os domínios com login DKIM habilitado têm um registro DNS " -"válido" +"Verifique se todos os domínios com login DKIM habilitado têm um registro DNS" +" válido" #: admin/app_settings.py:88 msgid "Enable DMARC checks" @@ -295,8 +292,8 @@ msgid "" "world." msgstr "" "Caminho absoluto do diretório onde as chaves privadas DKIM serão " -"armazenadas. Certifique-se que este diretório pertença ao usuário root e não " -"seja legível pelo mundo exterior." +"armazenadas. Certifique-se que este diretório pertença ao usuário root e não" +" seja legível pelo mundo exterior." #: admin/app_settings.py:137 msgid "Default DKIM key length" @@ -356,8 +353,8 @@ msgid "" "Default mailbox quota (in MB) applied to freshly created domains with no " "value specified. A value of 0 means no quota." msgstr "" -"Cota de caixa de correio padrão (em MB) aplicada para domínios recém-criados " -"sem nenhum valor especificado. Um valor 0 significa que não há cota." +"Cota de caixa de correio padrão (em MB) aplicada para domínios recém-criados" +" sem nenhum valor especificado. Um valor 0 significa que não há cota." #: admin/app_settings.py:197 msgid "Default mailbox sending limit" @@ -385,7 +382,8 @@ msgid "Automatic domain/mailbox creation" msgstr "Criação automática de domínio/caixa de correio" #: admin/app_settings.py:221 -msgid "Create a domain and a mailbox when an account is automatically created." +msgid "" +"Create a domain and a mailbox when an account is automatically created." msgstr "" "Crie um domínio e uma caixa de correio quando uma conta for criada " "automaticamente." @@ -716,7 +714,8 @@ msgid "" msgstr "" "A reputação do domínio será afetada e há uma chance de que os e-mails " "provenientes dele sejam considerados spam. Você deve entrar em contato com " -"esses provedores e pedir que eles retirem a lista de endereços IP detectados." +"esses provedores e pedir que eles retirem a lista de endereços IP " +"detectados." #: admin/templates/admin/notifications/domain_invalid_mx.html:3 #, python-format @@ -759,7 +758,8 @@ msgstr "Geral" #: autoreply/app_settings.py:18 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "Period between two auto-replies" msgstr "" "Tempo limite, em segundos, entre duas respostas automáticas para o mesmo " @@ -767,7 +767,8 @@ msgstr "" #: autoreply/app_settings.py:20 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "" "Number of days to wait before sending a new auto-reply to the same recipient" msgstr "" @@ -993,7 +994,8 @@ msgstr "Número de ciclos inválido" msgid "This code is invalid" msgstr "Este código é inválido" -#: core/api/v2/serializers.py:503 core/views/auth.py:194 core/views/auth.py:247 +#: core/api/v2/serializers.py:503 core/views/auth.py:194 +#: core/views/auth.py:247 msgid "Please use the following code to recover your Modoboa password: {}" msgstr "" "Por favor, use o seguinte código para recuperar sua senha do Modoboa: {}" @@ -1198,8 +1200,8 @@ msgstr "Grupos de administradores" #: core/app_settings.py:239 msgid "" -"Members of those LDAP Posix groups will be created as domain administrators. " -"Use ';' characters to separate groups." +"Members of those LDAP Posix groups will be created as domain administrators." +" Use ';' characters to separate groups." msgstr "" "Membros dos grupos do LDAP Posix serão criados como administradores de " "domínio. Use o caractere ';' para separar grupos." @@ -1249,8 +1251,8 @@ msgid "" "The distinguished name to use when binding to the LDAP server. Leave empty " "for an anonymous bind" msgstr "" -"O nome distinto para usar quando vincular ao servidor LDAP. Deixe vazio para " -"vincular anonimamente" +"O nome distinto para usar quando vincular ao servidor LDAP. Deixe vazio para" +" vincular anonimamente" #: core/app_settings.py:305 core/app_settings.py:367 msgid "Bind password" @@ -1290,8 +1292,8 @@ msgid "" "The template used to construct a user's DN. It should contain one " "placeholder (ie. %(user)s)" msgstr "" -"A template usada para construir um DN de usuário. Deve conter um placeholder " -"(por ex: %(user)s)" +"A template usada para construir um DN de usuário. Deve conter um placeholder" +" (por ex: %(user)s)" #: core/app_settings.py:350 msgid "LDAP synchronization settings" @@ -1305,8 +1307,8 @@ msgstr "Habilitar exportação para LDAP" msgid "" "Enable automatic synchronization between local database and LDAP directory" msgstr "" -"Ativar a sincronização automática entre o banco de dados local e o diretório " -"LDAP" +"Ativar a sincronização automática entre o banco de dados local e o diretório" +" LDAP" #: core/app_settings.py:389 msgid "Delete remote LDAP account when local account is deleted" @@ -1402,7 +1404,8 @@ msgstr "" #: core/app_settings.py:506 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed to super admins" msgstr "" "Mostrar um feed RSS personalizado para revendedores e administradores de " @@ -1410,7 +1413,8 @@ msgstr "" #: core/app_settings.py:509 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed also to super administrators" msgstr "" "Mostrar um feed RSS personalizado para revendedores e administradores de " @@ -1531,8 +1535,8 @@ msgstr "Ativar rastreamento de conta inativa" #: core/app_settings.py:686 msgid "" -"Allow the administrator to set a threshold (in days) beyond which an account " -"is considered inactive if the user hasn't logged in" +"Allow the administrator to set a threshold (in days) beyond which an account" +" is considered inactive if the user hasn't logged in" msgstr "" "Permitir que o administrador defina um limite (em dias) além do qual uma " "conta é considerada inativa se o usuário não tiver feito login" @@ -1543,8 +1547,8 @@ msgstr "Limite de conta inativa" #: core/app_settings.py:698 msgid "" -"An account with a last login date greater than this threshold (in days) will " -"be considered as inactive" +"An account with a last login date greater than this threshold (in days) will" +" be considered as inactive" msgstr "" "Uma conta com uma data de login maior que este limite (em dias) será " "considerada como inativa" @@ -1620,8 +1624,9 @@ msgstr "" #: core/checks.py:34 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"dovecot.html#dovecot-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-" +"oauth2" msgstr "" #: core/checks.py:41 @@ -1632,8 +1637,9 @@ msgstr "" #: core/checks.py:45 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"radicale.html#radicale-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-" +"oauth2" msgstr "" #: core/constants.py:6 @@ -2235,8 +2241,8 @@ msgstr "Apelidos de domínios" #: limits/app_settings.py:52 msgid "" -"Maximum number of allowed domain aliases for a new administrator. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain aliases for a new administrator. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Número máximo de apelidos de domínio permitidos para um novo administrador. " "(0 para negar qualquer criação, -1 para permitir criações ilimitadas)" @@ -2246,8 +2252,8 @@ msgid "" "Maximum number of allowed mailboxes for a new administrator. (0 to deny any " "creation, -1 to allow unlimited creations)" msgstr "" -"Número máximo de caixas de correio permitidas para um novo administrador. (0 " -"para negar qualquer criação, -1 para permitir criações ilimitadas)" +"Número máximo de caixas de correio permitidas para um novo administrador. (0" +" para negar qualquer criação, -1 para permitir criações ilimitadas)" #: limits/app_settings.py:73 limits/app_settings.py:146 limits/constants.py:39 #: limits/constants.py:92 @@ -2288,11 +2294,11 @@ msgstr "Habilitar ou desabilitar limites por domínio" #: limits/app_settings.py:114 msgid "" -"Maximum number of allowed domain administrators for a new domain. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain administrators for a new domain. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" -"Número máximo de administradores de domínio permitidos para um novo domínio. " -"(0 para negar qualquer criação, -1 para permitir criações ilimitadas)" +"Número máximo de administradores de domínio permitidos para um novo domínio." +" (0 para negar qualquer criação, -1 para permitir criações ilimitadas)" #: limits/app_settings.py:126 msgid "" @@ -2312,8 +2318,8 @@ msgstr "" #: limits/app_settings.py:149 msgid "" -"Maximum number of allowed aliases for a new domain. (0 to deny any creation, " -"-1 to allow unlimited creations)" +"Maximum number of allowed aliases for a new domain. (0 to deny any creation," +" -1 to allow unlimited creations)" msgstr "" "Número máximo de apelidos permitidos para um novo domínio. (0 para negar " "qualquer criação, -1 para permitir criações ilimitadas)" @@ -2539,8 +2545,9 @@ msgid "" "Generate a new document only when a new account is created. If set to no, a " "new document will be created each time a password is updated." msgstr "" -"Gere um novo documento somente quando uma nova conta for criada. Se definido " -"como não, um novo documento será criado sempre que uma senha for atualizada." +"Gere um novo documento somente quando uma nova conta for criada. Se definido" +" como não, um novo documento será criado sempre que uma senha for " +"atualizada." #: pdfcredentials/app_settings.py:84 msgid "Customization options" @@ -2579,8 +2586,8 @@ msgid "" "Include required SMTP and IMAP connection information to configure a mail " "client, a tablet or a phone" msgstr "" -"Incluir as informações de conexão SMTP e IMAP necessárias para configurar um " -"cliente de e-mail, um tablet ou um telefone" +"Incluir as informações de conexão SMTP e IMAP necessárias para configurar um" +" cliente de e-mail, um tablet ou um telefone" #: pdfcredentials/app_settings.py:123 pdfcredentials/documents.py:133 msgid "SMTP server address" @@ -2666,8 +2673,7 @@ msgid "" "your contacts.\n" msgstr "" "\n" -"Aqui você pode visualizar online seus e-mails a qualquer momento, criar " -"filtros ou gerenciar\n" +"Aqui você pode visualizar online seus e-mails a qualquer momento, criar filtros ou gerenciar\n" "seus contatos.\n" #: pdfcredentials/documents.py:126 @@ -2687,8 +2693,8 @@ msgid "" "Failed to create PDF_credentials directory. Please check the permissions or " "the path." msgstr "" -"Falha ao criar diretório PDF_credentials. Por favor, verifique as permissões " -"ou o caminho." +"Falha ao criar diretório PDF_credentials. Por favor, verifique as permissões" +" ou o caminho." #: pdfcredentials/handlers.py:64 msgid "Download PDF credentials" @@ -2771,8 +2777,8 @@ msgstr "" #: rspamd/app_settings.py:21 msgid "" -"Absolute path of the file which contains paths to DKIM private keys. Must be " -"readable by _rspamd group or user." +"Absolute path of the file which contains paths to DKIM private keys. Must be" +" readable by _rspamd group or user." msgstr "" #: rspamd/app_settings.py:30 @@ -2791,8 +2797,8 @@ msgstr "" #: rspamd/app_settings.py:53 msgid "" -"Location of the rspamd dashboard. Can either be a relative URL (eg. '/" -"rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " +"Location of the rspamd dashboard. Can either be a relative URL (eg. " +"'/rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " "guidance on the Super-Admin dashboard." msgstr "" @@ -2855,7 +2861,7 @@ msgstr "Qualquer um dos seguintes" #: sievefilters/constants.py:12 msgid "All messages" -msgstr "Todas as mensagens" +msgstr "Todas mensagens" #: sievefilters/constants.py:16 msgid "contains" @@ -3017,8 +3023,8 @@ msgid "" "primary address, and check your spam folder.

" msgstr "" "

Enviamos instruções por e-mail para definir sua senha, se houver uma " -"conta com o e-mail que você digitou. Você deve recebê-lo em breve.

Se " -"você não receber um e-mail, certifique-se de ter digitado seu endereço " +"conta com o e-mail que você digitou. Você deve recebê-lo em breve.

Se" +" você não receber um e-mail, certifique-se de ter digitado seu endereço " "principal e verifique sua pasta de spam.

" #: templates/registration/password_reset_email.html:4 @@ -3037,8 +3043,8 @@ msgid "" "If clicking the link above doesn't work, please copy and paste the URL in a " "new browser window instead." msgstr "" -"Se clicar no link acima não funcionar, copie e cole a URL em uma nova janela " -"do navegador." +"Se clicar no link acima não funcionar, copie e cole a URL em uma nova janela" +" do navegador." #: templates/registration/password_reset_email.html:14 #: templates/registration/password_reset_email_v2.html:14 @@ -3067,11 +3073,11 @@ msgstr "Senha de uso único" #: templates/registration/twofactor_code_verify.html:35 msgid "" -"Enter the code from the two-factor app on your mobile device. If you've lost " -"your device, you may enter one of your recovery codes." +"Enter the code from the two-factor app on your mobile device. If you've lost" +" your device, you may enter one of your recovery codes." msgstr "" -"Digite o código do aplicativo de dois fatores em seu celular. Se você perdeu " -"seu dispositivo, pode digitar um de seus códigos de recuperação." +"Digite o código do aplicativo de dois fatores em seu celular. Se você perdeu" +" seu dispositivo, pode digitar um de seus códigos de recuperação." #: templates/registration/twofactor_code_verify.html:42 msgid "Verify code" @@ -3335,6 +3341,5 @@ msgstr "Anexo muito grande (limite: %s)" #~ msgstr "IDs de alarme inválidos recebidos" #, fuzzy, python-brace-format -#~| msgid "Connection to IMAP server failed: %s" #~ msgid "Connection to IMAP server failed: {error}" #~ msgstr "Falha na conexão com o servidor IMAP: %s" diff --git a/modoboa/locale/pt_PT/LC_MESSAGES/django.po b/modoboa/locale/pt_PT/LC_MESSAGES/django.po index f8062ebfa..a99e36351 100644 --- a/modoboa/locale/pt_PT/LC_MESSAGES/django.po +++ b/modoboa/locale/pt_PT/LC_MESSAGES/django.po @@ -16,8 +16,7 @@ msgstr "" "POT-Creation-Date: 2025-08-28 09:41+0200\n" "PO-Revision-Date: 2024-07-17 11:45+0200\n" "Last-Translator: Mike C. \n" -"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/" -"modoboa/language/pt_PT/)\n" +"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/modoboa/language/pt_PT/)\n" "Language: pt_PT\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -211,8 +210,8 @@ msgstr "" #: admin/app_settings.py:37 msgid "" -"A list of IP or network address every MX record should match. A warning will " -"be sent if a record does not respect it." +"A list of IP or network address every MX record should match. A warning will" +" be sent if a record does not respect it." msgstr "" #: admin/app_settings.py:47 @@ -385,7 +384,8 @@ msgid "Automatic domain/mailbox creation" msgstr "Criação automatica de domino/caixa de correio" #: admin/app_settings.py:221 -msgid "Create a domain and a mailbox when an account is automatically created." +msgid "" +"Create a domain and a mailbox when an account is automatically created." msgstr "" "Criar um dominio e uma caixa de correio quando uma conta é criada " "automátiamente." @@ -775,7 +775,8 @@ msgstr "Geral" #: autoreply/app_settings.py:18 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "Period between two auto-replies" msgstr "" "Tempo limite em segundos entre duas respostas automáticas para o mesmo " @@ -783,7 +784,8 @@ msgstr "" #: autoreply/app_settings.py:20 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "" "Number of days to wait before sending a new auto-reply to the same recipient" msgstr "" @@ -1020,7 +1022,8 @@ msgstr "Número de linhas inválido" msgid "This code is invalid" msgstr "" -#: core/api/v2/serializers.py:503 core/views/auth.py:194 core/views/auth.py:247 +#: core/api/v2/serializers.py:503 core/views/auth.py:194 +#: core/views/auth.py:247 msgid "Please use the following code to recover your Modoboa password: {}" msgstr "" @@ -1234,8 +1237,8 @@ msgstr "Grupos de Administrador" #: core/app_settings.py:239 msgid "" -"Members of those LDAP Posix groups will be created as domain administrators. " -"Use ';' characters to separate groups." +"Members of those LDAP Posix groups will be created as domain administrators." +" Use ';' characters to separate groups." msgstr "" "Os membros destes grupos \"LDAP Posix\" serão criados como administradores " "de domínio. Use ';' para separar os grupos." @@ -1298,8 +1301,7 @@ msgstr "Password para bind" #: core/app_settings.py:307 core/app_settings.py:369 msgid "The password to use when binding to the LDAP server (with 'Bind DN')" -msgstr "" -"A password usada para fazer o \"bind\" ao servidor LDAP (com 'Bind DN')" +msgstr "A password usada para fazer o \"bind\" ao servidor LDAP (com 'Bind DN')" #: core/app_settings.py:317 core/app_settings.py:422 msgid "Users search base" @@ -1558,8 +1560,8 @@ msgstr "" #: core/app_settings.py:686 msgid "" -"Allow the administrator to set a threshold (in days) beyond which an account " -"is considered inactive if the user hasn't logged in" +"Allow the administrator to set a threshold (in days) beyond which an account" +" is considered inactive if the user hasn't logged in" msgstr "" #: core/app_settings.py:695 @@ -1568,8 +1570,8 @@ msgstr "" #: core/app_settings.py:698 msgid "" -"An account with a last login date greater than this threshold (in days) will " -"be considered as inactive" +"An account with a last login date greater than this threshold (in days) will" +" be considered as inactive" msgstr "" #: core/app_settings.py:707 @@ -1645,8 +1647,9 @@ msgstr "" #: core/checks.py:34 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"dovecot.html#dovecot-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-" +"oauth2" msgstr "" #: core/checks.py:41 @@ -1657,8 +1660,9 @@ msgstr "" #: core/checks.py:45 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"radicale.html#radicale-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-" +"oauth2" msgstr "" #: core/constants.py:6 @@ -1826,8 +1830,8 @@ msgstr "O simple utilizador '%s' deve ter endereço de e-mail válido" #| msgid "username and email fields must not differ for '%s'" msgid "username and email fields must not differ for '{}'" msgstr "" -"Os campos nome de utilizador e endereço de email não deve ser diferente para " -"'%s'" +"Os campos nome de utilizador e endereço de email não deve ser diferente para" +" '%s'" #: core/password_validation.py:25 msgid "Password must contain at least {} digit." @@ -1983,7 +1987,7 @@ msgstr "Nenhum" #, fuzzy #| msgid "Quantity" msgid "Quarantine" -msgstr "Quantidade" +msgstr "Quarentena" #: dmarc/models.py:12 #, fuzzy @@ -2288,10 +2292,11 @@ msgstr "Aliases de domínio" #: limits/app_settings.py:52 #, fuzzy -#| msgid "Maximum number of allowed domain aliases for a new administrator" +#| msgid "" +#| "Maximum number of allowed domain aliases for a new administrator" msgid "" -"Maximum number of allowed domain aliases for a new administrator. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain aliases for a new administrator. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Número máximo permitido de aliases de domínio para um novo administrador" @@ -2345,15 +2350,16 @@ msgstr "" #| msgid "" #| "Maximum number of allowed domain administrators for a new administrator" msgid "" -"Maximum number of allowed domain administrators for a new domain. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain administrators for a new domain. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Número máximo permitido de administradores de domínio para um novo " "administrador" #: limits/app_settings.py:126 #, fuzzy -#| msgid "Maximum number of allowed domain aliases for a new administrator" +#| msgid "" +#| "Maximum number of allowed domain aliases for a new administrator" msgid "" "Maximum number of allowed domain aliases for a new domain. (0 to deny any " "creation, -1 to allow unlimited creations)" @@ -2372,8 +2378,8 @@ msgstr "Número máximo permitido de caixas de correio para novo administrador" #, fuzzy #| msgid "Maximum number of allowed aliases for a new administrator" msgid "" -"Maximum number of allowed aliases for a new domain. (0 to deny any creation, " -"-1 to allow unlimited creations)" +"Maximum number of allowed aliases for a new domain. (0 to deny any creation," +" -1 to allow unlimited creations)" msgstr "Número máximo de aliases permitidas para um novo administrador" #: limits/apps.py:16 @@ -2395,7 +2401,8 @@ msgstr "Número máximo de caixas de correio que este utilizador por criar" #: limits/constants.py:40 msgid "Maximum number of mailbox aliases this user can create" msgstr "" -"Número máximo de aliases para caixas de correio que este utilizador por criar" +"Número máximo de aliases para caixas de correio que este utilizador por " +"criar" #: limits/constants.py:49 msgid "Maximum number of domain administrators this user can create" @@ -2423,7 +2430,8 @@ msgstr "Número máximo de caixas de correio que este utilizador por criar" #| msgid "Maximum number of mailbox aliases this user can create" msgid "Maximum number of mailbox aliases allowed for this domain." msgstr "" -"Número máximo de aliases para caixas de correio que este utilizador por criar" +"Número máximo de aliases para caixas de correio que este utilizador por " +"criar" #: limits/constants.py:102 #, fuzzy @@ -2833,8 +2841,8 @@ msgstr "" #: rspamd/app_settings.py:21 msgid "" -"Absolute path of the file which contains paths to DKIM private keys. Must be " -"readable by _rspamd group or user." +"Absolute path of the file which contains paths to DKIM private keys. Must be" +" readable by _rspamd group or user." msgstr "" #: rspamd/app_settings.py:30 @@ -2853,8 +2861,8 @@ msgstr "" #: rspamd/app_settings.py:53 msgid "" -"Location of the rspamd dashboard. Can either be a relative URL (eg. '/" -"rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " +"Location of the rspamd dashboard. Can either be a relative URL (eg. " +"'/rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " "guidance on the Super-Admin dashboard." msgstr "" @@ -3149,8 +3157,8 @@ msgstr "Password antiga" #: templates/registration/twofactor_code_verify.html:35 msgid "" -"Enter the code from the two-factor app on your mobile device. If you've lost " -"your device, you may enter one of your recovery codes." +"Enter the code from the two-factor app on your mobile device. If you've lost" +" your device, you may enter one of your recovery codes." msgstr "" #: templates/registration/twofactor_code_verify.html:42 @@ -3401,6 +3409,5 @@ msgid "Attachment is too big (limit: %s)" msgstr "Anexo demasiado grande (limite %s)" #, fuzzy, python-brace-format -#~| msgid "Connection to IMAP server failed: %s" #~ msgid "Connection to IMAP server failed: {error}" #~ msgstr "Ligação ao servidor IMAP falhou : %s" diff --git a/modoboa/locale/ru/LC_MESSAGES/django.po b/modoboa/locale/ru/LC_MESSAGES/django.po index 2dbc90f69..3658a8c76 100644 --- a/modoboa/locale/ru/LC_MESSAGES/django.po +++ b/modoboa/locale/ru/LC_MESSAGES/django.po @@ -17,15 +17,12 @@ msgstr "" "POT-Creation-Date: 2025-08-28 09:41+0200\n" "PO-Revision-Date: 2020-05-06 11:16+0000\n" "Last-Translator: Antoine Nguyen \n" -"Language-Team: Russian (http://www.transifex.com/tonio/modoboa/language/" -"ru/)\n" +"Language-Team: Russian (http://www.transifex.com/tonio/modoboa/language/ru/)\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || " -"(n%100>=11 && n%100<=14)? 2 : 3);\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" #: admin/api/v1/serializers.py:39 msgid "" @@ -200,8 +197,8 @@ msgstr "" #: admin/app_settings.py:37 msgid "" -"A list of IP or network address every MX record should match. A warning will " -"be sent if a record does not respect it." +"A list of IP or network address every MX record should match. A warning will" +" be sent if a record does not respect it." msgstr "" #: admin/app_settings.py:47 @@ -361,7 +358,8 @@ msgid "Automatic domain/mailbox creation" msgstr "Домен создан" #: admin/app_settings.py:221 -msgid "Create a domain and a mailbox when an account is automatically created." +msgid "" +"Create a domain and a mailbox when an account is automatically created." msgstr "" #: admin/app_settings.py:230 @@ -744,13 +742,15 @@ msgstr "Основные" #: autoreply/app_settings.py:18 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "Period between two auto-replies" msgstr "Промежуток между автоматическими ответами" #: autoreply/app_settings.py:20 #, fuzzy -#| msgid "Timeout in seconds between two auto-replies to the same recipient" +#| msgid "" +#| "Timeout in seconds between two auto-replies to the same recipient" msgid "" "Number of days to wait before sending a new auto-reply to the same recipient" msgstr "Промежуток между автоматическими ответами" @@ -978,7 +978,8 @@ msgstr "Неправильное количество циклов" msgid "This code is invalid" msgstr "" -#: core/api/v2/serializers.py:503 core/views/auth.py:194 core/views/auth.py:247 +#: core/api/v2/serializers.py:503 core/views/auth.py:194 +#: core/views/auth.py:247 msgid "Please use the following code to recover your Modoboa password: {}" msgstr "" @@ -1034,8 +1035,8 @@ msgid "" "Number of rounds to use (only used by sha256crypt and sha512crypt). Must be " "between 1000 and 999999999, inclusive." msgstr "" -"Количество циклов (используется только для sha256crypt и sha512crypt), Может " -"быть от 1000 до 999999999, включительно." +"Количество циклов (используется только для sha256crypt и sha512crypt), Может" +" быть от 1000 до 999999999, включительно." #: core/app_settings.py:73 msgid "Update password scheme at login" @@ -1180,8 +1181,8 @@ msgstr "Администратор груп" #: core/app_settings.py:239 msgid "" -"Members of those LDAP Posix groups will be created as domain administrators. " -"Use ';' characters to separate groups." +"Members of those LDAP Posix groups will be created as domain administrators." +" Use ';' characters to separate groups." msgstr "" "Члены этих LDAP Posix групп должны быть созданы администратором домена. " "Используйте ';' для как разделитель груп" @@ -1259,8 +1260,8 @@ msgid "" "An optional filter string (e.g. '(objectClass=person)'). In order to be " "valid, it must be enclosed in parentheses." msgstr "" -"Необязательная срока поиска (например '(objectClass=person)') Значение нужно " -"заключить в скобки" +"Необязательная срока поиска (например '(objectClass=person)') Значение нужно" +" заключить в скобки" #: core/app_settings.py:339 msgid "User DN template" @@ -1272,8 +1273,8 @@ msgid "" "The template used to construct a user's DN. It should contain one " "placeholder (ie. %(user)s)" msgstr "" -"Шаблон используется для построения запросов к DN Он должен содержать атрибут " -"например (%(user)s)" +"Шаблон используется для построения запросов к DN Он должен содержать атрибут" +" например (%(user)s)" #: core/app_settings.py:350 msgid "LDAP synchronization settings" @@ -1368,7 +1369,8 @@ msgstr "" #: core/app_settings.py:506 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed to super admins" msgstr "" "Отображать пользовательскую ленту RSS для реселлеров и администраторов " @@ -1376,7 +1378,8 @@ msgstr "" #: core/app_settings.py:509 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed also to super administrators" msgstr "" "Отображать пользовательскую ленту RSS для реселлеров и администраторов " @@ -1388,7 +1391,8 @@ msgstr "Спрятать виджет новых функций" #: core/app_settings.py:519 msgid "Hide features widget for resellers and domain administrators" -msgstr "Спрятать виджет новых функций для реселлеров и администраторов доменов" +msgstr "" +"Спрятать виджет новых функций для реселлеров и администраторов доменов" #: core/app_settings.py:531 msgid "Theme" @@ -1498,8 +1502,8 @@ msgstr "Порог неактивного аккаунта" #: core/app_settings.py:686 msgid "" -"Allow the administrator to set a threshold (in days) beyond which an account " -"is considered inactive if the user hasn't logged in" +"Allow the administrator to set a threshold (in days) beyond which an account" +" is considered inactive if the user hasn't logged in" msgstr "" #: core/app_settings.py:695 @@ -1508,8 +1512,8 @@ msgstr "Порог неактивного аккаунта" #: core/app_settings.py:698 msgid "" -"An account with a last login date greater than this threshold (in days) will " -"be considered as inactive" +"An account with a last login date greater than this threshold (in days) will" +" be considered as inactive" msgstr "" "Аккаунт с датой последнего входа, превышающей этот порог (в днях), будет " "считаться неактивным" @@ -1552,7 +1556,8 @@ msgstr "Порог перенаправления по умолчанию" #: core/app_settings.py:748 msgid "The default redirection used when no application is specified" -msgstr "Если приложение не указано. Используется перенаправление по умолчанию." +msgstr "" +"Если приложение не указано. Используется перенаправление по умолчанию." #: core/checks.py:15 msgid "" @@ -1581,8 +1586,9 @@ msgstr "" #: core/checks.py:34 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"dovecot.html#dovecot-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-" +"oauth2" msgstr "" #: core/checks.py:41 @@ -1593,8 +1599,9 @@ msgstr "" #: core/checks.py:45 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"radicale.html#radicale-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-" +"oauth2" msgstr "" #: core/constants.py:6 @@ -2202,10 +2209,11 @@ msgstr "Синонимы доменов" #: limits/app_settings.py:52 #, fuzzy -#| msgid "Maximum number of allowed domain aliases for a new administrator" +#| msgid "" +#| "Maximum number of allowed domain aliases for a new administrator" msgid "" -"Maximum number of allowed domain aliases for a new administrator. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain aliases for a new administrator. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "Максимальное количество синонимов доменов для нового администратора" #: limits/app_settings.py:64 @@ -2258,14 +2266,15 @@ msgstr "" #| msgid "" #| "Maximum number of allowed domain administrators for a new administrator" msgid "" -"Maximum number of allowed domain administrators for a new domain. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain administrators for a new domain. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Максимальное количество администраторов домена для нового администратора" #: limits/app_settings.py:126 #, fuzzy -#| msgid "Maximum number of allowed domain aliases for a new administrator" +#| msgid "" +#| "Maximum number of allowed domain aliases for a new administrator" msgid "" "Maximum number of allowed domain aliases for a new domain. (0 to deny any " "creation, -1 to allow unlimited creations)" @@ -2283,8 +2292,8 @@ msgstr "Максимальное количество почтовых ящик #, fuzzy #| msgid "Maximum number of allowed aliases for a new administrator" msgid "" -"Maximum number of allowed aliases for a new domain. (0 to deny any creation, " -"-1 to allow unlimited creations)" +"Maximum number of allowed aliases for a new domain. (0 to deny any creation," +" -1 to allow unlimited creations)" msgstr "Максимальное количество синонимов для нового администратора" #: limits/apps.py:16 @@ -2772,8 +2781,8 @@ msgstr "" #: rspamd/app_settings.py:21 msgid "" -"Absolute path of the file which contains paths to DKIM private keys. Must be " -"readable by _rspamd group or user." +"Absolute path of the file which contains paths to DKIM private keys. Must be" +" readable by _rspamd group or user." msgstr "" #: rspamd/app_settings.py:30 @@ -2792,8 +2801,8 @@ msgstr "" #: rspamd/app_settings.py:53 msgid "" -"Location of the rspamd dashboard. Can either be a relative URL (eg. '/" -"rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " +"Location of the rspamd dashboard. Can either be a relative URL (eg. " +"'/rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " "guidance on the Super-Admin dashboard." msgstr "" @@ -3080,8 +3089,8 @@ msgstr "Старый пароль" #: templates/registration/twofactor_code_verify.html:35 msgid "" -"Enter the code from the two-factor app on your mobile device. If you've lost " -"your device, you may enter one of your recovery codes." +"Enter the code from the two-factor app on your mobile device. If you've lost" +" your device, you may enter one of your recovery codes." msgstr "" #: templates/registration/twofactor_code_verify.html:42 @@ -3342,7 +3351,5 @@ msgid "Attachment is too big (limit: %s)" msgstr "Вложения слишком велики (лимит: %s)" #, fuzzy, python-brace-format -#~| msgid "Connection to MANAGESIEVE server failed, check your configuration" #~ msgid "Connection to IMAP server failed: {error}" -#~ msgstr "" -#~ "Соединение с сервером MANAGESIEVE не удалось, проверьте конфигурацию" +#~ msgstr "Соединение с сервером MANAGESIEVE не удалось, проверьте конфигурацию" diff --git a/modoboa/locale/sv/LC_MESSAGES/django.po b/modoboa/locale/sv/LC_MESSAGES/django.po index f0044ac6e..187bfb24d 100644 --- a/modoboa/locale/sv/LC_MESSAGES/django.po +++ b/modoboa/locale/sv/LC_MESSAGES/django.po @@ -12,10 +12,8 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-08-28 09:41+0200\n" "PO-Revision-Date: 2011-07-01 08:15+0000\n" -"Last-Translator: Olle Gustafsson , " -"2013,2015-2018,2020,2022-2023,2025\n" -"Language-Team: Swedish (http://app.transifex.com/tonio/modoboa/language/" -"sv/)\n" +"Last-Translator: Olle Gustafsson , 2013,2015-2018,2020,2022-2023,2025\n" +"Language-Team: Swedish (http://app.transifex.com/tonio/modoboa/language/sv/)\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -27,8 +25,8 @@ msgid "" "Quota shared between mailboxes. Can be expressed in KB, MB (default) or GB. " "A value of 0 means no quota." msgstr "" -"Kvot som delas mellan brevlådor. Kan uttryckas i KB, MB (standard) eller GB. " -"Ett värde på 0 betyder ingen kvot." +"Kvot som delas mellan brevlådor. Kan uttryckas i KB, MB (standard) eller GB." +" Ett värde på 0 betyder ingen kvot." #: admin/api/v1/serializers.py:46 msgid "Default quota in MB applied to mailboxes. A value of 0 means no quota." @@ -184,8 +182,8 @@ msgstr "Giltiga MX" #: admin/app_settings.py:37 msgid "" -"A list of IP or network address every MX record should match. A warning will " -"be sent if a record does not respect it." +"A list of IP or network address every MX record should match. A warning will" +" be sent if a record does not respect it." msgstr "" "En lista med IP- eller nätverksadresser som varje MX-post ska matcha. En " "varning kommer att skickas om en post inte respekterar den." @@ -199,8 +197,8 @@ msgid "" "Prevent the creation of a new domain if its MX record does not use one of " "the defined addresses." msgstr "" -"Förhindra skapandet av en ny domän om dess MX-post inte använder någon av de " -"definierade adresserna." +"Förhindra skapandet av en ny domän om dess MX-post inte använder någon av de" +" definierade adresserna." #: admin/app_settings.py:59 msgid "Enable IPV6 checks" @@ -344,8 +342,8 @@ msgid "" "Number of messages freshly created mailboxes will be allowed to send per " "day. Leave empty for no limit." msgstr "" -"Antal meddelanden som nyskapade brevlådor tillåts skicka per dag. Lämna tomt " -"utan gräns." +"Antal meddelanden som nyskapade brevlådor tillåts skicka per dag. Lämna tomt" +" utan gräns." #: admin/app_settings.py:207 msgid "Automatic account removal" @@ -360,7 +358,8 @@ msgid "Automatic domain/mailbox creation" msgstr "Automatisk domän-/brevlådeskapande" #: admin/app_settings.py:221 -msgid "Create a domain and a mailbox when an account is automatically created." +msgid "" +"Create a domain and a mailbox when an account is automatically created." msgstr "Skapa en domän och en postlåda när ett konto skapas automatiskt." #: admin/app_settings.py:230 @@ -681,8 +680,8 @@ msgid "" "coming from it are considered as spam. You should contact those providers " "and ask them to unlist detected IP address(es)." msgstr "" -"Domänens rykte kommer att påverkas och det finns en chans att e-" -"postmeddelanden som kommer från den betraktas som spam. Du bör kontakta " +"Domänens rykte kommer att påverkas och det finns en chans att " +"e-postmeddelanden som kommer från den betraktas som spam. Du bör kontakta " "dessa leverantörer och be dem att ta bort upptäckta IP-adresser." #: admin/templates/admin/notifications/domain_invalid_mx.html:3 @@ -915,7 +914,8 @@ msgstr "2FA redan aktiverat" #| msgid "Failed TFA settings editing attempt from '%s' as user '%s'" msgid "Failed TFA settings editing attempt from '%(addr)s' as user '%(user)s'" msgstr "" -"Misslyckades TFA-inställningar redigeringsförsök från '%s' som användare '%s'" +"Misslyckades TFA-inställningar redigeringsförsök från '%s' som användare " +"'%s'" #: core/api/v1/serializers.py:91 core/api/v2/serializers.py:445 msgid "Invalid password" @@ -946,7 +946,8 @@ msgstr "Ogiltigt antal omgångar" msgid "This code is invalid" msgstr "Denna kod är ogiltig" -#: core/api/v2/serializers.py:503 core/views/auth.py:194 core/views/auth.py:247 +#: core/api/v2/serializers.py:503 core/views/auth.py:194 +#: core/views/auth.py:247 msgid "Please use the following code to recover your Modoboa password: {}" msgstr "Använd följande kod för att återställa ditt Modoboa lösenord: {}" @@ -1150,8 +1151,8 @@ msgstr "Administratörsgrupper" #: core/app_settings.py:239 msgid "" -"Members of those LDAP Posix groups will be created as domain administrators. " -"Use ';' characters to separate groups." +"Members of those LDAP Posix groups will be created as domain administrators." +" Use ';' characters to separate groups." msgstr "" "Användare av dessa LDAP Posix grupper kommer skapas som " "domänadministratörer. Använd ';' för att separera grupper." @@ -1243,8 +1244,8 @@ msgid "" "The template used to construct a user's DN. It should contain one " "placeholder (ie. %(user)s)" msgstr "" -"Mallen som används för att konstruera en användares DN. Den bör innehålla en " -"platshållare (dvs. %(user)s)" +"Mallen som används för att konstruera en användares DN. Den bör innehålla en" +" platshållare (dvs. %(user)s)" #: core/app_settings.py:350 msgid "LDAP synchronization settings" @@ -1269,7 +1270,8 @@ msgid "" "Delete remote LDAP account when local account is deleted, otherwise it will " "be disabled." msgstr "" -"Radera LDAP-fjärrkonto när det lokala kontot raderas, annars inaktiveras det." +"Radera LDAP-fjärrkonto när det lokala kontot raderas, annars inaktiveras " +"det." #: core/app_settings.py:401 msgid "Account DN template" @@ -1306,7 +1308,8 @@ msgstr "Aktivera Dovecot LDAP-synkronisering" #: core/app_settings.py:457 msgid "LDAP authentication settings will be applied to Dovecot configuration." -msgstr "LDAP-autentiseringsinställningar tillämpas på Dovecot-konfigurationen." +msgstr "" +"LDAP-autentiseringsinställningar tillämpas på Dovecot-konfigurationen." #: core/app_settings.py:465 msgid "Dovecot LDAP config file" @@ -1344,21 +1347,26 @@ msgstr "Användardefinierat RSS-flöde" #: core/app_settings.py:497 msgid "Display custom RSS feed to resellers and domain administrators" msgstr "" -"Visa användardefinierat RSS-flöde för återförsäljare och domänadministratörer" +"Visa användardefinierat RSS-flöde för återförsäljare och " +"domänadministratörer" #: core/app_settings.py:506 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed to super admins" msgstr "" -"Visa användardefinierat RSS-flöde för återförsäljare och domänadministratörer" +"Visa användardefinierat RSS-flöde för återförsäljare och " +"domänadministratörer" #: core/app_settings.py:509 #, fuzzy -#| msgid "Display custom RSS feed to resellers and domain administrators" +#| msgid "" +#| "Display custom RSS feed to resellers and domain administrators" msgid "Display custom RSS feed also to super administrators" msgstr "" -"Visa användardefinierat RSS-flöde för återförsäljare och domänadministratörer" +"Visa användardefinierat RSS-flöde för återförsäljare och " +"domänadministratörer" #: core/app_settings.py:517 msgid "Hide features widget" @@ -1474,8 +1482,8 @@ msgstr "Aktivera spårning av inaktiva konton" #: core/app_settings.py:686 msgid "" -"Allow the administrator to set a threshold (in days) beyond which an account " -"is considered inactive if the user hasn't logged in" +"Allow the administrator to set a threshold (in days) beyond which an account" +" is considered inactive if the user hasn't logged in" msgstr "" "Tillåt administratören att ställa in en tröskel (i dagar) över vilken ett " "konto anses inaktivt om användaren inte har loggat in" @@ -1486,8 +1494,8 @@ msgstr "Gräns för inaktiva konton" #: core/app_settings.py:698 msgid "" -"An account with a last login date greater than this threshold (in days) will " -"be considered as inactive" +"An account with a last login date greater than this threshold (in days) will" +" be considered as inactive" msgstr "" "Ett konto som inte har loggat in innan denna gräns (antal dagar) kommer " "anses som inaktivt" @@ -1562,8 +1570,9 @@ msgstr "" #: core/checks.py:34 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"dovecot.html#dovecot-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-" +"oauth2" msgstr "" #: core/checks.py:41 @@ -1574,8 +1583,9 @@ msgstr "" #: core/checks.py:45 msgid "" -"Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/" -"radicale.html#radicale-oauth2" +"Visit : " +"https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-" +"oauth2" msgstr "" #: core/constants.py:6 @@ -1751,7 +1761,8 @@ msgstr[1] "Lösenord måste innehålla minst {} specialtecken." #: core/password_validation.py:70 msgid "Your password must contain a combination of different character types." -msgstr "Ditt lösenord måste innehålla en kombination av olika typer av tecken." +msgstr "" +"Ditt lösenord måste innehålla en kombination av olika typer av tecken." #: core/sms_backends/ovh.py:21 core/sms_backends/ovh.py:102 msgid "API endpoint" @@ -2141,8 +2152,8 @@ msgid "" "Maximum number of allowed domain administrators for a new administrator. (0 " "to deny any creation, -1 to allow unlimited creations)" msgstr "" -"Maximalt antal tillåtna domänadministratörer för en ny administratör. (0 för " -"att neka någon skapelse, -1 för att tillåta obegränsade skapelser)" +"Maximalt antal tillåtna domänadministratörer för en ny administratör. (0 för" +" att neka någon skapelse, -1 för att tillåta obegränsade skapelser)" #: limits/app_settings.py:41 msgid "" @@ -2159,8 +2170,8 @@ msgstr "Domän alias" #: limits/app_settings.py:52 msgid "" -"Maximum number of allowed domain aliases for a new administrator. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain aliases for a new administrator. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Maximalt antal tillåtna domänalias för en ny administratör. (0 för att neka " "någon skapelse, -1 för att tillåta obegränsade skapelser)" @@ -2183,8 +2194,8 @@ msgid "" "Maximum number of allowed aliases for a new administrator. (0 to deny any " "creation, -1 to allow unlimited creations)" msgstr "" -"Maximalt antal tillåtna alias för en ny administratör. (0 för att neka någon " -"skapelse, -1 för att tillåta obegränsade skapelser)" +"Maximalt antal tillåtna alias för en ny administratör. (0 för att neka någon" +" skapelse, -1 för att tillåta obegränsade skapelser)" #: limits/app_settings.py:84 limits/constants.py:59 msgid "Quota" @@ -2212,8 +2223,8 @@ msgstr "Aktivera eller inaktivera gränser per domän" #: limits/app_settings.py:114 msgid "" -"Maximum number of allowed domain administrators for a new domain. (0 to deny " -"any creation, -1 to allow unlimited creations)" +"Maximum number of allowed domain administrators for a new domain. (0 to deny" +" any creation, -1 to allow unlimited creations)" msgstr "" "Maximalt antal tillåtna domänadministratörer för en ny domän. (0 för att " "neka någon skapelse, -1 för att tillåta obegränsade skapelser)" @@ -2236,8 +2247,8 @@ msgstr "" #: limits/app_settings.py:149 msgid "" -"Maximum number of allowed aliases for a new domain. (0 to deny any creation, " -"-1 to allow unlimited creations)" +"Maximum number of allowed aliases for a new domain. (0 to deny any creation," +" -1 to allow unlimited creations)" msgstr "" "Maximalt antal tillåtna alias för en ny domän. (0 för att neka någon " "skapelse, -1 för att tillåta obegränsade skapelser)" @@ -2456,8 +2467,8 @@ msgid "" "Generate a new document only when a new account is created. If set to no, a " "new document will be created each time a password is updated." msgstr "" -"Skapa ett nytt dokument först när ett nytt konto har skapats. Om inställt på " -"nej kommer ett nytt dokument att skapas varje gång ett lösenord uppdateras." +"Skapa ett nytt dokument först när ett nytt konto har skapats. Om inställt på" +" nej kommer ett nytt dokument att skapas varje gång ett lösenord uppdateras." #: pdfcredentials/app_settings.py:84 msgid "Customization options" @@ -2583,8 +2594,7 @@ msgid "" "your contacts.\n" msgstr "" "\n" -"Här kan du se dina e-postmeddelanden när som helst online, skapa filter " -"eller hantera\n" +"Här kan du se dina e-postmeddelanden när som helst online, skapa filter eller hantera\n" "dina kontakter.\n" #: pdfcredentials/documents.py:126 @@ -2685,11 +2695,11 @@ msgstr "Sökväg till nyckelkarta" #: rspamd/app_settings.py:21 msgid "" -"Absolute path of the file which contains paths to DKIM private keys. Must be " -"readable by _rspamd group or user." +"Absolute path of the file which contains paths to DKIM private keys. Must be" +" readable by _rspamd group or user." msgstr "" -"Absolut sökväg till filen som innehåller sökvägar till DKIM privata nycklar. " -"Måste vara läsbar av _rspamd-gruppen eller användaren." +"Absolut sökväg till filen som innehåller sökvägar till DKIM privata nycklar." +" Måste vara läsbar av _rspamd-gruppen eller användaren." #: rspamd/app_settings.py:30 msgid "Selector map path" @@ -2709,8 +2719,8 @@ msgstr "Plats för Rspamd-dashboard" #: rspamd/app_settings.py:53 msgid "" -"Location of the rspamd dashboard. Can either be a relative URL (eg. '/" -"rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " +"Location of the rspamd dashboard. Can either be a relative URL (eg. " +"'/rspamd') or a full URL (eg. 'https://rspamd.domain.tld/').Only use for " "guidance on the Super-Admin dashboard." msgstr "" "Plats för Rspamd-dashboarden. Kan vara en relativ URL (t.ex. '/rspamd') " @@ -2939,9 +2949,9 @@ msgid "" "primary address, and check your spam folder.

" msgstr "" "

Vi har e-postat dig instruktioner för att ändra ditt lösenord, om ett " -"konto finns med den e-post adress du angav. Du borde få detta brev inom kort." -"

Om du inte fått något brev, var vänlig kontrollera att du angav rätt " -"e-post adress, och kolla din skräpkatalog.

" +"konto finns med den e-post adress du angav. Du borde få detta brev inom " +"kort.

Om du inte fått något brev, var vänlig kontrollera att du angav" +" rätt e-post adress, och kolla din skräpkatalog.

" #: templates/registration/password_reset_email.html:4 #: templates/registration/password_reset_email_v2.html:4 @@ -2950,8 +2960,8 @@ msgid "" "To initiate the password reset process for your %(username)s Modoboa " "account, click the link below:" msgstr "" -"För att starta processen för återställning av lösenord för ditt %(username)s " -"Modoboa konto, klicka på länken nedanför:" +"För att starta processen för återställning av lösenord för ditt %(username)s" +" Modoboa konto, klicka på länken nedanför:" #: templates/registration/password_reset_email.html:10 #: templates/registration/password_reset_email_v2.html:10 @@ -2989,8 +2999,8 @@ msgstr "Engångslösenord" #: templates/registration/twofactor_code_verify.html:35 msgid "" -"Enter the code from the two-factor app on your mobile device. If you've lost " -"your device, you may enter one of your recovery codes." +"Enter the code from the two-factor app on your mobile device. If you've lost" +" your device, you may enter one of your recovery codes." msgstr "" "Ange koden från tvåfaktorsappen på din mobila enhet. Om du har tappat bort " "din enhet kan du ange en av dina återställningskoder." @@ -3062,7 +3072,7 @@ msgstr "Servern behöver autentisering" #: webmail/app_settings.py:118 msgid "Display" -msgstr "Visa" +msgstr "Visning" #: webmail/app_settings.py:124 msgid "Default message display mode" @@ -3082,7 +3092,7 @@ msgstr "Aktivera/inaktivera HTML-länkvisning" #: webmail/app_settings.py:144 msgid "Number of displayed emails per page" -msgstr "Antal visade e-postmeddelanden per sida" +msgstr "Antalet visade e-postmeddelanden per sida" #: webmail/app_settings.py:147 msgid "Sets the maximum number of messages displayed in a page" diff --git a/modoboa/test_settings.py b/modoboa/test_settings.py index 1e7457356..3556a9b6a 100644 --- a/modoboa/test_settings.py +++ b/modoboa/test_settings.py @@ -43,6 +43,12 @@ elif DB == "sqlite": "PORT": "", "ATOMIC_REQUESTS": True, }, + "amavis": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": "amavis.db", + "PORT": "", + "ATOMIC_REQUESTS": True, + }, } else: DATABASES = { diff --git a/test_project/test_project/settings.py b/test_project/test_project/settings.py index 2fbb5e308..cf0c1d3c1 100644 --- a/test_project/test_project/settings.py +++ b/test_project/test_project/settings.py @@ -392,13 +392,19 @@ WEBMAIL_DEV_PASSWORD = os.environ.get("WEBMAIL_DEV_PASSWORD", "") # Amavis -DATABASES["amavis"] = { # NOQA - "ENGINE": "django.db.backends.mysql", - "HOST": "mariadb", - "NAME": "amavis", - "USER": "root", - "PASSWORD": "Password1000", -} +MIGRATION_MODULES = {"amavis": None} +TEST_RUNNER = "modoboa.lib.test_runners.UnManagedModelTestRunner" + +# Uncomment this in dev mode +# DATABASES["amavis"] = { # NOQA +# "ENGINE": "django.db.backends.mysql", +# "HOST": "mariadb", +# "NAME": "amavis", +# "USER": "root", +# "PASSWORD": "Password1000", +# } DATABASE_ROUTERS = ["modoboa.amavis.dbrouter.AmavisRouter"] -AMAVIS_DEFAULT_DATABASE_ENCODING = "LATIN1" + +AMAVIS_DEFAULT_DATABASE_ENCODING = "UTF-8" +# AMAVIS_DEFAULT_DATABASE_ENCODING = "LATIN1"