From 332ffefd5c42d6b387b00834d0d167b3531c173c Mon Sep 17 00:00:00 2001 From: sturlan Date: Fri, 27 Mar 2026 12:54:00 +0100 Subject: [PATCH 001/228] feat(thread): show per-message To/Cc/Bcc recipients in thread view Closes #11305, #9311, #4258. Recipients (To, Cc, Bcc) are already returned by the API for every message in a thread but were never displayed in ThreadEnvelope. Add RecipientBubble components in the expanded message header for each recipient list, following the same pattern used for per-message subject-change display. AI-assisted: Claude Code (claude-sonnet-4-6) Signed-off-by: Darko Sturlan --- src/components/ThreadEnvelope.vue | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/components/ThreadEnvelope.vue b/src/components/ThreadEnvelope.vue index 3078e2e0be..5dd83c8bff 100644 --- a/src/components/ThreadEnvelope.vue +++ b/src/components/ThreadEnvelope.vue @@ -68,6 +68,32 @@ {{ envelope.from && envelope.from[0] ? envelope.from[0].email : '' }}

+
{{ cleanSubject }}
@@ -341,6 +367,7 @@ import IconFavorite from 'vue-material-design-icons/Star.vue' import StarOutline from 'vue-material-design-icons/StarOutline.vue' import DeleteIcon from 'vue-material-design-icons/TrashCanOutline.vue' import Avatar from './Avatar.vue' +import RecipientBubble from './RecipientBubble.vue' import ConfirmModal from './ConfirmationModal.vue' import Error from './Error.vue' import EventModal from './EventModal.vue' @@ -389,6 +416,7 @@ export default { TranslationModal, ConfirmModal, Avatar, + RecipientBubble, NcActionButton, NcButton, Error, @@ -1314,6 +1342,21 @@ export default { white-space: nowrap; } + .recipients { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 4px; + margin-block: 2px; + margin-inline-start: 8px; + + &__label { + color: var(--color-text-maxcontrast); + font-weight: bold; + white-space: nowrap; + } + } + &--expanded { min-height: 350px; } From 971a59dec9534917cb608c90fd27a9adfe992e83 Mon Sep 17 00:00:00 2001 From: sturlan Date: Fri, 27 Mar 2026 13:02:41 +0100 Subject: [PATCH 002/228] fix(thread): move recipient bubbles outside router-link to fix click handling RecipientBubble uses NcUserBubble which renders as an anchor tag. Placing it inside the router-link's click handler caused navigation on click instead of opening the contact popover. Move the recipients section outside the router-link into its own envelope__recipients div. AI-assisted: Claude Code (claude-sonnet-4-6) Signed-off-by: Darko Sturlan --- src/components/ThreadEnvelope.vue | 92 +++++++++++++++++-------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/src/components/ThreadEnvelope.vue b/src/components/ThreadEnvelope.vue index 5dd83c8bff..2dfcd74354 100644 --- a/src/components/ThreadEnvelope.vue +++ b/src/components/ThreadEnvelope.vue @@ -68,33 +68,7 @@ {{ envelope.from && envelope.from[0] ? envelope.from[0].email : '' }}

- -
+
{{ cleanSubject }}
@@ -297,7 +271,33 @@
- +
+
+ {{ t('mail', 'To:') }} + +
+
+ {{ t('mail', 'Cc:') }} + +
+
+ {{ t('mail', 'Bcc:') }} + +
+
+ Date: Fri, 27 Mar 2026 13:05:49 +0100 Subject: [PATCH 003/228] feat(thread): match design spec from #4258 Per ChristophWurst's Sept 2023 design spec: - Hide sender email address in collapsed message state - Apply --color-text-maxcontrast to sender name when expanded - Remove thread-level participant bubble header from Thread.vue along with all associated dead code (participantsToDisplay, moreParticipantsString, updateParticipantsToDisplay, resize listener, RecipientBubble/NcPopover imports) AI-assisted: Claude Code (claude-sonnet-4-6) Signed-off-by: Darko Sturlan --- src/components/Thread.vue | 95 +------------------------------ src/components/ThreadEnvelope.vue | 12 ++-- 2 files changed, 10 insertions(+), 97 deletions(-) diff --git a/src/components/Thread.vue b/src/components/Thread.vue index 2a7aa1d160..3d7d72cf55 100644 --- a/src/components/Thread.vue +++ b/src/components/Thread.vue @@ -16,39 +16,7 @@

{{ threadSubject }}

-
- - - - - - - - - -
+ @@ -73,13 +41,10 @@ + + diff --git a/src/components/NavigationAccount.vue b/src/components/NavigationAccount.vue index 4bedb0027e..5d9ae21c62 100644 --- a/src/components/NavigationAccount.vue +++ b/src/components/NavigationAccount.vue @@ -36,6 +36,18 @@ {{ t('mail', 'Account settings') }} + + + {{ t('mail', 'Delegate account') }} + {{ t('mail', 'Move down') }} - + @@ -85,6 +97,7 @@ + @@ -92,7 +105,7 @@ import { DialogBuilder, showError } from '@nextcloud/dialogs' import { formatFileSize } from '@nextcloud/files' import { generateUrl } from '@nextcloud/router' -import { NcActionButton as ActionButton, NcActionCheckbox as ActionCheckbox, NcActionInput as ActionInput, NcActionText as ActionText, NcLoadingIcon as IconLoading, NcAppNavigationCaption } from '@nextcloud/vue' +import { NcActionButton as ActionButton, NcActionCheckbox as ActionCheckbox, NcActionInput as ActionInput, NcActionText as ActionText, NcLoadingIcon as IconLoading, NcAppNavigationCaption, NcIconSvgWrapper } from '@nextcloud/vue' import { mapStores } from 'pinia' import { Fragment } from 'vue-frag' import MenuDown from 'vue-material-design-icons/ChevronDown.vue' @@ -104,6 +117,7 @@ import IconDelete from 'vue-material-design-icons/TrashCanOutline.vue' import logger from '../logger.js' import { fetchQuota } from '../service/AccountService.js' import useMainStore from '../store/mainStore.js' +import IconDelegation from './../../img/delegation.svg' export default { name: 'NavigationAccount', @@ -115,8 +129,10 @@ export default { ActionInput, ActionText, AccountSettings: () => import(/* webpackChunkName: "account-settings" */ './AccountSettings.vue'), + DelegationModal: () => import(/* webpackChunkName: "delegation-modal" */ './DelegationModal.vue'), IconInfo, IconSettings, + NcIconSvgWrapper, IconFolderAdd, MenuDown, MenuUp, @@ -162,6 +178,8 @@ export default { quota: undefined, editing: false, showSaving: false, + showDelegationModal: false, + IconDelegation, createMailboxName: '', showMailboxes: false, nameInput: false, @@ -179,6 +197,10 @@ export default { return this.account.isUnified !== true && this.account.visible !== false }, + canDelegate() { + return !this.account.isDelegated && !this.account.provisioningId + }, + id() { return 'account-' + this.account.id }, @@ -253,6 +275,7 @@ export default { location.href = generateUrl('/apps/mail') } catch (error) { logger.error('could not delete account', { error }) + showError(t('mail', 'could not delete account')) } finally { this.loading.delete = false } diff --git a/src/service/DelegationService.js b/src/service/DelegationService.js new file mode 100644 index 0000000000..919e3e6d00 --- /dev/null +++ b/src/service/DelegationService.js @@ -0,0 +1,59 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import axios from '@nextcloud/axios' +import { generateUrl } from '@nextcloud/router' + +/** + * @typedef Delegation + * @property {number} id the delegation id + * @property {number} accountId the account id + * @property {string} userId the delegated user id + */ + +/** + * Fetch all users that have delegation for a given account + * + * @param {number} accountId id of the account + * @return {Promise} + */ +export async function fetchDelegatedUsers(accountId) { + const url = generateUrl('/apps/mail/api/delegations/{accountId}', { + accountId, + }) + const { data } = await axios.get(url) + return data +} + +/** + * Delegate an account to a user + * + * @param {number} accountId id of the account + * @param {string} userId id of the user to delegate to + * @return {Promise} + */ +export async function delegate(accountId, userId) { + const url = generateUrl('/apps/mail/api/delegations/{accountId}', { + accountId, + }) + const { data } = await axios.post(url, { userId }) + return data +} + +/** + * Revoke delegation of an account for a user + * + * @param {number} accountId id of the account + * @param {string} userId id of the user to revoke delegation for + * @return {Promise} + */ +export async function unDelegate(accountId, userId) { + const url = generateUrl('/apps/mail/api/delegations/{accountId}/{userId}', { + accountId, + userId, + }) + + const { data } = await axios.delete(url) + return data +} diff --git a/tests/Integration/MailboxSynchronizationTest.php b/tests/Integration/MailboxSynchronizationTest.php index aa93fdf616..ba7c5b9a24 100644 --- a/tests/Integration/MailboxSynchronizationTest.php +++ b/tests/Integration/MailboxSynchronizationTest.php @@ -16,6 +16,7 @@ use OCA\Mail\Controller\MailboxesController; use OCA\Mail\Db\MessageMapper as DbMessageMapper; use OCA\Mail\Service\AccountService; +use OCA\Mail\Service\DelegationService; use OCA\Mail\Service\Sync\ImapToDbSynchronizer; use OCA\Mail\Service\Sync\SyncService; use OCA\Mail\Tests\Integration\Framework\ImapTest; @@ -51,6 +52,7 @@ protected function setUp(): void { Server::get(SyncService::class), Server::get(IConfig::class), Server::get(ITimeFactory::class), + Server::get(DelegationService::class), ); $this->account = $this->createTestAccount('user12345'); diff --git a/tests/Unit/Controller/AccountApiControllerTest.php b/tests/Unit/Controller/AccountApiControllerTest.php index c560c8e4f5..010c08219c 100644 --- a/tests/Unit/Controller/AccountApiControllerTest.php +++ b/tests/Unit/Controller/AccountApiControllerTest.php @@ -16,6 +16,7 @@ use OCA\Mail\Db\MailAccount; use OCA\Mail\Service\AccountService; use OCA\Mail\Service\AliasesService; +use OCA\Mail\Service\DelegationService; use OCP\AppFramework\Http; use OCP\IRequest; use PHPUnit\Framework\MockObject\MockObject; @@ -28,6 +29,7 @@ class AccountApiControllerTest extends TestCase { private IRequest&MockObject $request; private AccountService&MockObject $accountService; private AliasesService&MockObject $aliasesService; + private DelegationService&MockObject $delegationService; protected function setUp(): void { parent::setUp(); @@ -35,6 +37,7 @@ protected function setUp(): void { $this->request = $this->createMock(IRequest::class); $this->accountService = $this->createMock(AccountService::class); $this->aliasesService = $this->createMock(AliasesService::class); + $this->delegationService = $this->createMock(DelegationService::class); $this->controller = new AccountApiController( 'mail', @@ -42,6 +45,7 @@ protected function setUp(): void { self::USER_ID, $this->accountService, $this->aliasesService, + $this->delegationService, ); } @@ -52,6 +56,7 @@ public function testListWithoutUser() { null, $this->accountService, $this->aliasesService, + $this->delegationService, ); $this->accountService->expects(self::never()) @@ -68,12 +73,17 @@ public function testList() { $mailAccount = new MailAccount(); $mailAccount->setId(42); $mailAccount->setEmail('foo@bar.com'); + $mailAccount->setUserId(self::USER_ID); $account = new Account($mailAccount); $this->accountService->expects(self::once()) ->method('findByUserId') ->with(self::USER_ID) ->willReturn([$account]); + $this->accountService->expects(self::once()) + ->method('findDelegatedAccounts') + ->with(self::USER_ID) + ->willReturn([]); $alias = new Alias(); $alias->setId(10); @@ -90,6 +100,7 @@ public function testList() { [ 'id' => 42, 'email' => 'foo@bar.com', + 'isDelegated' => false, 'aliases' => [ [ 'id' => 10, @@ -105,12 +116,17 @@ public function testListWithAliasWithoutName() { $mailAccount = new MailAccount(); $mailAccount->setId(42); $mailAccount->setEmail('foo@bar.com'); + $mailAccount->setUserId(self::USER_ID); $account = new Account($mailAccount); $this->accountService->expects(self::once()) ->method('findByUserId') ->with(self::USER_ID) ->willReturn([$account]); + $this->accountService->expects(self::once()) + ->method('findDelegatedAccounts') + ->with(self::USER_ID) + ->willReturn([]); $alias = new Alias(); $alias->setId(10); @@ -127,6 +143,7 @@ public function testListWithAliasWithoutName() { [ 'id' => 42, 'email' => 'foo@bar.com', + 'isDelegated' => false, 'aliases' => [ [ 'id' => 10, @@ -138,16 +155,91 @@ public function testListWithAliasWithoutName() { ], $actual->getData()); } + public function testListWithDelegatedAccounts() { + $ownMailAccount = new MailAccount(); + $ownMailAccount->setId(42); + $ownMailAccount->setEmail('foo@bar.com'); + $ownMailAccount->setUserId(self::USER_ID); + $ownAccount = new Account($ownMailAccount); + + + $delegatedMailAccount = new MailAccount(); + $delegatedMailAccount->setId(99); + $delegatedMailAccount->setEmail('shared@bar.com'); + $delegatedMailAccount->setUserId('owner'); + $delegatedAccount = new Account($delegatedMailAccount); + + $this->accountService->expects(self::once()) + ->method('findByUserId') + ->with(self::USER_ID) + ->willReturn([$ownAccount]); + $this->accountService->expects(self::once()) + ->method('findDelegatedAccounts') + ->with(self::USER_ID) + ->willReturn([$delegatedAccount]); + + $ownAlias = new Alias(); + $ownAlias->setId(10); + $ownAlias->setName('Baz'); + $ownAlias->setAlias('baz@bar.com'); + + $delegatedAlias = new Alias(); + $delegatedAlias->setId(20); + $delegatedAlias->setName('Shared Alias'); + $delegatedAlias->setAlias('shared-alias@bar.com'); + + $this->aliasesService->expects(self::exactly(2)) + ->method('findAll') + ->willReturnMap([ + [42, self::USER_ID, [$ownAlias]], + [99, 'owner', [$delegatedAlias]], + ]); + + $actual = $this->controller->list(); + $this->assertEquals(Http::STATUS_OK, $actual->getStatus()); + $this->assertEquals([ + [ + 'id' => 42, + 'email' => 'foo@bar.com', + 'isDelegated' => false, + 'aliases' => [ + [ + 'id' => 10, + 'email' => 'baz@bar.com', + 'name' => 'Baz', + ], + ], + ], + [ + 'id' => 99, + 'email' => 'shared@bar.com', + 'isDelegated' => true, + 'aliases' => [ + [ + 'id' => 20, + 'email' => 'shared-alias@bar.com', + 'name' => 'Shared Alias', + ], + ], + ], + ], $actual->getData()); + } + public function testListWithoutAliases() { $mailAccount = new MailAccount(); $mailAccount->setId(42); $mailAccount->setEmail('foo@bar.com'); + $mailAccount->setUserId(self::USER_ID); $account = new Account($mailAccount); $this->accountService->expects(self::once()) ->method('findByUserId') ->with(self::USER_ID) ->willReturn([$account]); + $this->accountService->expects(self::once()) + ->method('findDelegatedAccounts') + ->with(self::USER_ID) + ->willReturn([]); $this->aliasesService->expects(self::once()) ->method('findAll') @@ -160,6 +252,7 @@ public function testListWithoutAliases() { [ 'id' => 42, 'email' => 'foo@bar.com', + 'isDelegated' => false, 'aliases' => [], ] ], $actual->getData()); diff --git a/tests/Unit/Controller/AccountsControllerTest.php b/tests/Unit/Controller/AccountsControllerTest.php index b659555d3c..234ca139a6 100644 --- a/tests/Unit/Controller/AccountsControllerTest.php +++ b/tests/Unit/Controller/AccountsControllerTest.php @@ -21,6 +21,7 @@ use OCA\Mail\IMAP\Sync\Response; use OCA\Mail\Service\AccountService; use OCA\Mail\Service\AliasesService; +use OCA\Mail\Service\DelegationService; use OCA\Mail\Service\SetupService; use OCA\Mail\Service\Sync\SyncService; use OCP\AppFramework\Db\DoesNotExistException; @@ -85,6 +86,9 @@ class AccountsControllerTest extends TestCase { /** @var IConfig|(IConfig&MockObject)|MockObject */ private IConfig|MockObject $config; + + /** @var DelegationService|MockObject */ + private $delegationService; /** @var IRemoteHostValidator|MockObject */ private $hostValidator; @@ -107,6 +111,9 @@ protected function setUp(): void { $this->hostValidator = $this->createMock(IRemoteHostValidator::class); $this->hostValidator->method('isValid')->willReturn(true); $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->delegationService = $this->createMock(DelegationService::class); + $this->delegationService->method('resolveAccountUserId') + ->willReturn($this->userId); $this->controller = new AccountsController( $this->appName, @@ -124,6 +131,7 @@ protected function setUp(): void { $this->hostValidator, $this->mailboxSync, $this->timeFactory, + $this->delegationService, ); $this->account = $this->createMock(Account::class); $this->accountId = 123; @@ -143,6 +151,10 @@ public function testIndex(): void { ->method('findAll') ->with(self::equalTo($this->accountId), self::equalTo($this->userId)) ->will(self::returnValue(['a1', 'a2'])); + $this->accountService->expects(self::once()) + ->method('findDelegatedAccounts') + ->with(self::equalTo($this->userId)) + ->willReturn([]); $response = $this->controller->index(); @@ -153,6 +165,7 @@ public function testIndex(): void { 'a1', 'a2', ], + 'isDelegated' => false, ] ]); self::assertEquals($expectedResponse, $response); diff --git a/tests/Unit/Controller/AliasesControllerTest.php b/tests/Unit/Controller/AliasesControllerTest.php index 51e36e7933..067cecda14 100644 --- a/tests/Unit/Controller/AliasesControllerTest.php +++ b/tests/Unit/Controller/AliasesControllerTest.php @@ -15,6 +15,7 @@ use OCA\Mail\Exception\ClientException; use OCA\Mail\Exception\NotImplemented; use OCA\Mail\Service\AliasesService; +use OCA\Mail\Service\DelegationService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; @@ -35,6 +36,9 @@ class AliasesControllerTest extends TestCase { /** @var AliasesService */ private $aliasService; + /** @var DelegationService */ + private $delegationService; + public function setUp(): void { parent::setUp(); $this->request = $this->getMockBuilder('OCP\IRequest') @@ -48,7 +52,12 @@ public function setUp(): void { $this->mailAccountMapper = $this->createMock(MailAccountMapper::class); $this->aliasService = new AliasesService($this->aliasMapper, $this->mailAccountMapper); - $this->controller = new AliasesController($this->appName, $this->request, $this->aliasService, $this->userId); + $this->delegationService = $this->createMock(DelegationService::class); + $this->delegationService->method('resolveAccountUserId') + ->willReturn($this->userId); + $this->delegationService->method('resolveAliasUserId') + ->willReturn($this->userId); + $this->controller = new AliasesController($this->appName, $this->request, $this->aliasService, $this->userId, $this->delegationService); } public function testIndex(): void { diff --git a/tests/Unit/Controller/DelegationControllerTest.php b/tests/Unit/Controller/DelegationControllerTest.php new file mode 100644 index 0000000000..e0a6798c71 --- /dev/null +++ b/tests/Unit/Controller/DelegationControllerTest.php @@ -0,0 +1,260 @@ +request = $this->createMock(IRequest::class); + $this->delegationService = $this->createMock(DelegationService::class); + $this->accountService = $this->createMock(AccountService::class); + $this->userManager = $this->createMock(IUserManager::class); + + $this->controller = new DelegationController( + $this->appName, + $this->request, + $this->delegationService, + $this->accountService, + $this->userManager, + $this->currentUserId, + ); + + $ownMailAccount = new MailAccount(); + $ownMailAccount->setId(1); + $ownMailAccount->setUserId($this->currentUserId); + $ownMailAccount->setEmail('owner@example.com'); + $this->ownAccount = new Account($ownMailAccount); + + $otherMailAccount = new MailAccount(); + $otherMailAccount->setId(2); + $otherMailAccount->setUserId('other'); + $otherMailAccount->setEmail('other@example.com'); + $this->otherAccount = new Account($otherMailAccount); + } + + public function testGetDelegatedUsersSuccess(): void { + $delegation = new Delegation(); + $delegation->setId(10); + $delegation->setAccountId(1); + $delegation->setUserId('delegatee'); + + $this->accountService->expects($this->once()) + ->method('findById') + ->with(1) + ->willReturn($this->ownAccount); + + $this->delegationService->expects($this->once()) + ->method('findDelegatedToUsersForAccount') + ->with(1) + ->willReturn([$delegation]); + + $response = $this->controller->getDelegatedUsers(1); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals(Http::STATUS_OK, $response->getStatus()); + $this->assertEquals([$delegation], $response->getData()); + } + + public function testGetDelegatedUsersUnauthorized(): void { + $this->accountService->expects($this->once()) + ->method('findById') + ->with(2) + ->willReturn($this->otherAccount); + + $this->delegationService->expects($this->never()) + ->method('findDelegatedToUsersForAccount'); + + $response = $this->controller->getDelegatedUsers(2); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals(Http::STATUS_UNAUTHORIZED, $response->getStatus()); + } + + public function testDelegateSuccess(): void { + $delegation = new Delegation(); + $delegation->setId(10); + $delegation->setAccountId(1); + $delegation->setUserId('delegatee'); + + $this->accountService->expects($this->once()) + ->method('findById') + ->with(1) + ->willReturn($this->ownAccount); + + $this->userManager->expects($this->once()) + ->method('userExists') + ->with('delegatee') + ->willReturn(true); + + $this->delegationService->expects($this->once()) + ->method('delegate') + ->with(1, 'delegatee') + ->willReturn($delegation); + + $response = $this->controller->delegate(1, 'delegatee'); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); + $this->assertEquals($delegation, $response->getData()); + } + + public function testDelegateUnauthorized(): void { + $this->accountService->expects($this->once()) + ->method('findById') + ->with(2) + ->willReturn($this->otherAccount); + + $this->delegationService->expects($this->never()) + ->method('delegate'); + + $response = $this->controller->delegate(2, 'delegatee'); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals(Http::STATUS_UNAUTHORIZED, $response->getStatus()); + } + + public function testDelegateProvisionedAccount(): void { + $provisionedMailAccount = new MailAccount(); + $provisionedMailAccount->setId(3); + $provisionedMailAccount->setUserId($this->currentUserId); + $provisionedMailAccount->setEmail('provisioned@example.com'); + $provisionedMailAccount->setProvisioningId(42); + $provisionedAccount = new Account($provisionedMailAccount); + + $this->accountService->expects($this->once()) + ->method('findById') + ->with(3) + ->willReturn($provisionedAccount); + + $this->delegationService->expects($this->never()) + ->method('delegate'); + + $response = $this->controller->delegate(3, 'delegatee'); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals(Http::STATUS_FORBIDDEN, $response->getStatus()); + $this->assertEquals(['message' => 'Cannot delegate provisioned accounts'], $response->getData()); + } + + public function testDelegateToSelf(): void { + $this->accountService->expects($this->never()) + ->method('findById'); + + $this->delegationService->expects($this->never()) + ->method('delegate'); + + $response = $this->controller->delegate(1, $this->currentUserId); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals(Http::STATUS_BAD_REQUEST, $response->getStatus()); + $this->assertEquals(['message' => 'Cannot delegate to yourself'], $response->getData()); + } + + public function testDelegateUserNotFound(): void { + $this->accountService->expects($this->once()) + ->method('findById') + ->with(1) + ->willReturn($this->ownAccount); + + $this->userManager->expects($this->once()) + ->method('userExists') + ->with('nonexistent') + ->willReturn(false); + + $this->delegationService->expects($this->never()) + ->method('delegate'); + + $response = $this->controller->delegate(1, 'nonexistent'); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus()); + } + + public function testDelegateAlreadyExists(): void { + $this->accountService->expects($this->once()) + ->method('findById') + ->with(1) + ->willReturn($this->ownAccount); + + $this->userManager->expects($this->once()) + ->method('userExists') + ->with('delegatee') + ->willReturn(true); + + $this->delegationService->expects($this->once()) + ->method('delegate') + ->with(1, 'delegatee') + ->willThrowException(new DelegationExistsException('Delegation already exists')); + + $response = $this->controller->delegate(1, 'delegatee'); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals(Http::STATUS_CONFLICT, $response->getStatus()); + $this->assertEquals(['message' => 'Delegation already exists'], $response->getData()); + } + + public function testUnDelegateSuccess(): void { + $this->accountService->expects($this->once()) + ->method('findById') + ->with(1) + ->willReturn($this->ownAccount); + + $this->delegationService->expects($this->once()) + ->method('unDelegate') + ->with(1, 'delegatee'); + + $response = $this->controller->unDelegate(1, 'delegatee'); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals(Http::STATUS_OK, $response->getStatus()); + } + + public function testUnDelegateUnauthorized(): void { + $this->accountService->expects($this->once()) + ->method('findById') + ->with(2) + ->willReturn($this->otherAccount); + + $this->delegationService->expects($this->never()) + ->method('unDelegate'); + + $response = $this->controller->unDelegate(2, 'delegatee'); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals(Http::STATUS_UNAUTHORIZED, $response->getStatus()); + } +} diff --git a/tests/Unit/Controller/DraftsControllerTest.php b/tests/Unit/Controller/DraftsControllerTest.php index e1f672aff1..d9651ff6fa 100644 --- a/tests/Unit/Controller/DraftsControllerTest.php +++ b/tests/Unit/Controller/DraftsControllerTest.php @@ -19,6 +19,7 @@ use OCA\Mail\Exception\ServiceException; use OCA\Mail\Http\JsonResponse; use OCA\Mail\Service\AccountService; +use OCA\Mail\Service\DelegationService; use OCA\Mail\Service\DraftsService; use OCA\Mail\Service\SmimeService; use OCP\AppFramework\Db\DoesNotExistException; @@ -35,6 +36,7 @@ class DraftsControllerTest extends TestCase { private AccountService $accountService; private DraftsController $controller; private SmimeService $smimeService; + private DelegationService $delegationService; protected function setUp(): void { parent::setUp(); @@ -46,6 +48,11 @@ protected function setUp(): void { $this->accountService = $this->createMock(AccountService::class); $this->timeFactory = $this->createMock(ITimeFactory::class); $this->smimeService = $this->createMock(SmimeService::class); + $this->delegationService = $this->createMock(DelegationService::class); + $this->delegationService->method('resolveAccountUserId') + ->willReturn($this->userId); + $this->delegationService->method('resolveLocalMessageUserId') + ->willReturn($this->userId); $this->controller = new DraftsController( $this->appName, @@ -54,7 +61,8 @@ protected function setUp(): void { $this->service, $this->accountService, $this->timeFactory, - $this->smimeService + $this->smimeService, + $this->delegationService, ); } diff --git a/tests/Unit/Controller/ListControllerTest.php b/tests/Unit/Controller/ListControllerTest.php index 259ded1d51..b97b9dd482 100644 --- a/tests/Unit/Controller/ListControllerTest.php +++ b/tests/Unit/Controller/ListControllerTest.php @@ -34,6 +34,9 @@ protected function setUp(): void { $this->serviceMock = $this->createServiceMock(ListController::class, [ 'userId' => 'user123', ]); + $this->serviceMock->getParameter('delegationService') + ->method('resolveMessageUserId') + ->willReturn('user123'); $this->controller = $this->serviceMock->getService(); } diff --git a/tests/Unit/Controller/MailboxesApiControllerTest.php b/tests/Unit/Controller/MailboxesApiControllerTest.php index 221de5de87..f00754af70 100644 --- a/tests/Unit/Controller/MailboxesApiControllerTest.php +++ b/tests/Unit/Controller/MailboxesApiControllerTest.php @@ -19,6 +19,7 @@ use OCA\Mail\Db\Message as DbMessage; use OCA\Mail\Folder; use OCA\Mail\Service\AccountService; +use OCA\Mail\Service\DelegationService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; use OCP\IRequest; @@ -33,6 +34,7 @@ class MailboxesApiControllerTest extends TestCase { private IMailManager|MockObject $mailManager; private AccountService&MockObject $accountService; private MockObject|IMailSearch $mailSearch; + private DelegationService&MockObject $delegationService; protected function setUp(): void { parent::setUp(); @@ -41,6 +43,11 @@ protected function setUp(): void { $this->accountService = $this->createMock(AccountService::class); $this->mailManager = $this->createMock(IMailManager::class); $this->mailSearch = $this->createMock(IMailSearch::class); + $this->delegationService = $this->createMock(DelegationService::class); + $this->delegationService->method('resolveAccountUserId') + ->willReturn(self::USER_ID); + $this->delegationService->method('resolveMailboxUserId') + ->willReturn(self::USER_ID); $this->controller = new MailboxesApiController( 'mail', @@ -49,7 +56,7 @@ protected function setUp(): void { $this->mailManager, $this->accountService, $this->mailSearch, - + $this->delegationService, ); } @@ -61,6 +68,7 @@ public function testListMailboxesWithoutUser() { $this->mailManager, $this->accountService, $this->mailSearch, + $this->delegationService, ); $this->accountService->expects(self::never()) @@ -101,6 +109,7 @@ public function testListMessagesWithoutUser() { $this->mailManager, $this->accountService, $this->mailSearch, + $this->delegationService, ); $this->accountService->expects(self::never()) diff --git a/tests/Unit/Controller/MailboxesControllerTest.php b/tests/Unit/Controller/MailboxesControllerTest.php index 8dddb381d2..51ffc5d3ef 100644 --- a/tests/Unit/Controller/MailboxesControllerTest.php +++ b/tests/Unit/Controller/MailboxesControllerTest.php @@ -18,6 +18,7 @@ use OCA\Mail\Folder; use OCA\Mail\IMAP\MailboxStats; use OCA\Mail\Service\AccountService; +use OCA\Mail\Service\DelegationService; use OCA\Mail\Service\Sync\SyncService; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Utility\ITimeFactory; @@ -49,6 +50,7 @@ class MailboxesControllerTest extends TestCase { private IConfig|MockObject $config; private ITimeFactory|MockObject $timeFactory; + private DelegationService|MockObject $delegationService; public function setUp(): void { parent::setUp(); @@ -59,6 +61,9 @@ public function setUp(): void { $this->syncService = $this->createMock(SyncService::class); $this->config = $this->createMock(IConfig::class); $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->delegationService = $this->createMock(DelegationService::class); + $this->delegationService->method('resolveAccountUserId')->willReturn($this->userId); + $this->delegationService->method('resolveMailboxUserId')->willReturn($this->userId); $this->controller = new MailboxesController( $this->appName, @@ -68,7 +73,8 @@ public function setUp(): void { $this->mailManager, $this->syncService, $this->config, - $this->timeFactory + $this->timeFactory, + $this->delegationService, ); } diff --git a/tests/Unit/Controller/MessageApiControllerTest.php b/tests/Unit/Controller/MessageApiControllerTest.php index 7b06ac01d1..6ab43d2dea 100644 --- a/tests/Unit/Controller/MessageApiControllerTest.php +++ b/tests/Unit/Controller/MessageApiControllerTest.php @@ -27,6 +27,7 @@ use OCA\Mail\Service\AccountService; use OCA\Mail\Service\AliasesService; use OCA\Mail\Service\Attachment\AttachmentService; +use OCA\Mail\Service\DelegationService; use OCA\Mail\Service\DkimService; use OCA\Mail\Service\ItineraryService; use OCA\Mail\Service\MailManager; @@ -58,6 +59,7 @@ class MessageApiControllerTest extends TestCase { private DkimService|MockObject $dkimService; private MockObject|ItineraryService $itineraryService; private TrustedSenderService|MockObject $trustedSenderService; + private DelegationService|MockObject $delegationService; private MessageApiController $controller; private string $fromEmail = 'john@test.com'; private int $accountId = 1; @@ -84,6 +86,9 @@ protected function setUp(): void { $this->dkimService = $this->createMock(DkimService::class); $this->itineraryService = $this->createMock(ItineraryService::class); $this->trustedSenderService = $this->createMock(TrustedSenderService::class); + $this->delegationService = $this->createMock(DelegationService::class); + $this->delegationService->method('resolveAccountUserId')->willReturn($this->userId); + $this->delegationService->method('resolveMessageUserId')->willReturn($this->userId); $this->controller = new MessageApiController($this->appName, $this->userId, @@ -100,6 +105,7 @@ protected function setUp(): void { $this->dkimService, $this->itineraryService, $this->trustedSenderService, + $this->delegationService, ); $mailAccount = new MailAccount(); diff --git a/tests/Unit/Controller/MessagesControllerTest.php b/tests/Unit/Controller/MessagesControllerTest.php index 83837b63c6..227a73dc90 100644 --- a/tests/Unit/Controller/MessagesControllerTest.php +++ b/tests/Unit/Controller/MessagesControllerTest.php @@ -37,6 +37,7 @@ use OCA\Mail\Model\Message; use OCA\Mail\Service\AccountService; use OCA\Mail\Service\AiIntegrations\AiIntegrationsService; +use OCA\Mail\Service\DelegationService; use OCA\Mail\Service\ItineraryService; use OCA\Mail\Service\MailManager; use OCA\Mail\Service\SmimeService; @@ -134,6 +135,8 @@ class MessagesControllerTest extends TestCase { private ICacheFactory&MockObject $cacheFactory; + private DelegationService|MockObject $delegationService; + protected function setUp(): void { parent::setUp(); @@ -164,6 +167,10 @@ protected function setUp(): void { $this->cacheFactory->method('createDistributed') ->willReturn(new NullCache()); + $this->delegationService = $this->createMock(DelegationService::class); + $this->delegationService->method('resolveMessageUserId')->willReturn($this->userId); + $this->delegationService->method('resolveMailboxUserId')->willReturn($this->userId); + $timeFactory = $this->createMocK(ITimeFactory::class); $timeFactory->expects($this->any()) ->method('getTime') @@ -194,6 +201,7 @@ protected function setUp(): void { $this->snoozeService, $this->aiIntegrationsService, $this->cacheFactory, + $this->delegationService, ); $this->account = $this->createMock(Account::class); @@ -1250,6 +1258,7 @@ public function testNeedsTranslationNoUser() { $this->snoozeService, $this->aiIntegrationsService, $this->cacheFactory, + $this->delegationService, ); $actualResponse = $controller->needsTranslation(100); @@ -1422,6 +1431,7 @@ public function testSmartReplyNoUser(): void { $this->snoozeService, $this->aiIntegrationsService, $this->cacheFactory, + $this->delegationService, ); $actualResponse = $controller->smartReply(100); diff --git a/tests/Unit/Controller/OutboxControllerTest.php b/tests/Unit/Controller/OutboxControllerTest.php index 51f25603cf..ff31d91315 100644 --- a/tests/Unit/Controller/OutboxControllerTest.php +++ b/tests/Unit/Controller/OutboxControllerTest.php @@ -19,6 +19,7 @@ use OCA\Mail\Exception\ServiceException; use OCA\Mail\Http\JsonResponse; use OCA\Mail\Service\AccountService; +use OCA\Mail\Service\DelegationService; use OCA\Mail\Service\OutboxService; use OCA\Mail\Service\SmimeService; use OCP\AppFramework\Db\DoesNotExistException; @@ -43,6 +44,8 @@ class OutboxControllerTest extends TestCase { /** @var SmimeService&MockObject */ private $smimeService; + private DelegationService&MockObject $delegationService; + private OutboxController $controller; protected function setUp(): void { parent::setUp(); @@ -53,6 +56,11 @@ protected function setUp(): void { $this->request = $this->createMock(IRequest::class); $this->accountService = $this->createMock(AccountService::class); $this->smimeService = $this->createMock(SmimeService::class); + $this->delegationService = $this->createMock(DelegationService::class); + $this->delegationService->method('resolveAccountUserId') + ->willReturn($this->userId); + $this->delegationService->method('resolveLocalMessageUserId') + ->willReturn($this->userId); $this->controller = new OutboxController( $this->appName, @@ -61,6 +69,7 @@ protected function setUp(): void { $this->service, $this->accountService, $this->smimeService, + $this->delegationService, ); } diff --git a/tests/Unit/Controller/PageControllerTest.php b/tests/Unit/Controller/PageControllerTest.php index 804b9df2ef..9b84226810 100644 --- a/tests/Unit/Controller/PageControllerTest.php +++ b/tests/Unit/Controller/PageControllerTest.php @@ -205,6 +205,10 @@ public function testIndex(): void { $account1, $account2, ])); + $this->accountService->expects($this->once()) + ->method('findDelegatedAccounts') + ->with($this->userId) + ->willReturn([]); $this->mailManager->expects($this->exactly(2)) ->method('getMailboxes') ->withConsecutive( @@ -247,6 +251,7 @@ public function testIndex(): void { 'mailboxes' => [ $mailbox, ], + 'isDelegated' => false, ], [ 'accountId' => 2, @@ -255,6 +260,7 @@ public function testIndex(): void { 'a22', ], 'mailboxes' => [], + 'isDelegated' => false, ], ]; diff --git a/tests/Unit/Controller/SieveControllerTest.php b/tests/Unit/Controller/SieveControllerTest.php index c084c716ab..9a7bde01b9 100644 --- a/tests/Unit/Controller/SieveControllerTest.php +++ b/tests/Unit/Controller/SieveControllerTest.php @@ -43,6 +43,9 @@ protected function setUp(): void { 'userId' => '1', ] ); + $this->serviceMock->getParameter('delegationService') + ->method('resolveAccountUserId') + ->willReturn('1'); $this->sieveController = $this->serviceMock->getService(); } diff --git a/tests/Unit/Controller/ThreadControllerTest.php b/tests/Unit/Controller/ThreadControllerTest.php index bdd8220b7b..1dcf67c695 100644 --- a/tests/Unit/Controller/ThreadControllerTest.php +++ b/tests/Unit/Controller/ThreadControllerTest.php @@ -19,6 +19,7 @@ use OCA\Mail\Model\EventData; use OCA\Mail\Service\AccountService; use OCA\Mail\Service\AiIntegrations\AiIntegrationsService; +use OCA\Mail\Service\DelegationService; use OCA\Mail\Service\SnoozeService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; @@ -54,6 +55,9 @@ class ThreadControllerTest extends TestCase { /** @var LoggerInterface|MockObject */ private $logger; + /** @var DelegationService|MockObject */ + private $delegationService; + protected function setUp(): void { parent::setUp(); @@ -65,6 +69,9 @@ protected function setUp(): void { $this->snoozeService = $this->createMock(SnoozeService::class); $this->aiIntergrationsService = $this->createMock(AiIntegrationsService::class); $this->logger = $this->createMock(LoggerInterface::class); + $this->delegationService = $this->createMock(DelegationService::class); + $this->delegationService->method('resolveMessageUserId')->willReturn($this->userId); + $this->delegationService->method('resolveMailboxUserId')->willReturn($this->userId); $this->controller = new ThreadController( $this->appName, @@ -75,6 +82,7 @@ protected function setUp(): void { $this->snoozeService, $this->aiIntergrationsService, $this->logger, + $this->delegationService, ); } diff --git a/tests/Unit/Service/AccountServiceTest.php b/tests/Unit/Service/AccountServiceTest.php index 05df2f5cd9..d2b9e7f1e5 100644 --- a/tests/Unit/Service/AccountServiceTest.php +++ b/tests/Unit/Service/AccountServiceTest.php @@ -13,6 +13,7 @@ use OCA\Mail\Account; use OCA\Mail\BackgroundJob\QuotaJob; use OCA\Mail\BackgroundJob\SyncJob; +use OCA\Mail\Db\DelegationMapper; use OCA\Mail\Db\MailAccount; use OCA\Mail\Db\MailAccountMapper; use OCA\Mail\Exception\ClientException; @@ -61,6 +62,7 @@ class AccountServiceTest extends TestCase { private IConfig&MockObject $config; private ITimeFactory&MockObject $time; + private DelegationMapper&MockObject $delegationMapper; protected function setUp(): void { parent::setUp(); @@ -72,6 +74,7 @@ protected function setUp(): void { $this->imapClientFactory = $this->createMock(IMAPClientFactory::class); $this->config = $this->createMock(IConfig::class); $this->time = $this->createMock(ITimeFactory::class); + $this->delegationMapper = $this->createMock(DelegationMapper::class); $this->accountService = new AccountService( $this->mapper, $this->aliasesService, @@ -79,6 +82,7 @@ protected function setUp(): void { $this->imapClientFactory, $this->config, $this->time, + $this->delegationMapper, ); $this->account1 = new MailAccount(); diff --git a/tests/Unit/Service/DelegationServiceTest.php b/tests/Unit/Service/DelegationServiceTest.php new file mode 100644 index 0000000000..c0ab17becf --- /dev/null +++ b/tests/Unit/Service/DelegationServiceTest.php @@ -0,0 +1,313 @@ +delegationMapper = $this->createMock(DelegationMapper::class); + $this->accountService = $this->createMock(AccountService::class); + $this->mailboxMapper = $this->createMock(MailboxMapper::class); + $this->messageMapper = $this->createMock(MessageMapper::class); + $this->aliasMapper = $this->createMock(AliasMapper::class); + $this->localMessageMapper = $this->createMock(LocalMessageMapper::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->notificationManager = $this->createMock(IManager::class); + $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + + $this->service = new DelegationService( + $this->delegationMapper, + $this->accountService, + $this->mailboxMapper, + $this->messageMapper, + $this->aliasMapper, + $this->localMessageMapper, + ); + + $mailAccount = new MailAccount(); + $mailAccount->setId(1); + $mailAccount->setUserId('owner'); + $mailAccount->setEmail('owner@example.com'); + $this->account = new Account($mailAccount); + } + + private function mockNotification(): void { + $notification = $this->createMock(INotification::class); + $notification->method('setApp')->willReturnSelf(); + $notification->method('setUser')->willReturnSelf(); + $notification->method('setObject')->willReturnSelf(); + $notification->method('setSubject')->willReturnSelf(); + $notification->method('setDateTime')->willReturnSelf(); + $notification->method('setMessage')->willReturnSelf(); + + $this->notificationManager->method('createNotification')->willReturn($notification); + + $user = $this->createMock(IUser::class); + $user->method('getDisplayName')->willReturn('Owner User'); + $this->userManager->method('get')->with('owner')->willReturn($user); + $this->timeFactory->method('getDateTime')->willReturn(new \DateTime()); + } + + public function testDelegateSuccess(): void { + $this->mockNotification(); + + $this->delegationMapper->expects($this->once()) + ->method('find') + ->with(1, 'delegatee') + ->willThrowException(new DoesNotExistException('Not found')); + + $expected = new Delegation(); + $expected->setAccountId(1); + $expected->setUserId('delegatee'); + + $this->delegationMapper->expects($this->once()) + ->method('insert') + ->willReturnCallback(function (Delegation $d) { + $d->setId(10); + return $d; + }); + + $result = $this->service->delegate($this->account->getId(), 'delegatee'); + + $this->assertEquals(1, $result->getAccountId()); + $this->assertEquals('delegatee', $result->getUserId()); + } + + public function testDelegateThrowsWhenAlreadyExists(): void { + $existing = new Delegation(); + $existing->setAccountId(1); + $existing->setUserId('delegatee'); + + $this->delegationMapper->expects($this->once()) + ->method('find') + ->with(1, 'delegatee') + ->willReturn($existing); + + $this->delegationMapper->expects($this->never()) + ->method('insert'); + + $this->expectException(DelegationExistsException::class); + + $this->service->delegate($this->account->getId(), 'delegatee'); + } + + public function testFindDelegatedToUsersForAccount(): void { + $delegation = new Delegation(); + $delegation->setAccountId(1); + $delegation->setUserId('delegatee'); + + $this->delegationMapper->expects($this->once()) + ->method('findDelegatedToUsers') + ->with(1) + ->willReturn([$delegation]); + + $result = $this->service->findDelegatedToUsersForAccount(1); + + $this->assertCount(1, $result); + $this->assertEquals('delegatee', $result[0]->getUserId()); + } + + public function testUnDelegateSuccess(): void { + $this->mockNotification(); + + $delegation = new Delegation(); + $delegation->setId(10); + $delegation->setAccountId(1); + $delegation->setUserId('delegatee'); + + $this->delegationMapper->expects($this->once()) + ->method('find') + ->with(1, 'delegatee') + ->willReturn($delegation); + + $this->delegationMapper->expects($this->once()) + ->method('delete') + ->with($delegation); + + $this->service->unDelegate($this->account->getId(), 'delegatee'); + } + + public function testUnDelegateWhenNotFound(): void { + $this->delegationMapper->expects($this->once()) + ->method('find') + ->with(1, 'delegatee') + ->willThrowException(new DoesNotExistException('Not found')); + + $this->delegationMapper->expects($this->never()) + ->method('delete'); + + $this->service->unDelegate($this->account->getId(), 'delegatee'); + } + + public function testResolveAccountUserIdOwner(): void { + $mailAccount = new MailAccount(); + $mailAccount->setId(1); + $mailAccount->setUserId('owner'); + + $this->accountService->expects($this->once()) + ->method('find') + ->with('owner', 1) + ->willReturn(new Account($mailAccount)); + + $result = $this->service->resolveAccountUserId(1, 'owner'); + + $this->assertEquals('owner', $result); + } + + public function testResolveAccountUserIdDelegated(): void { + $this->accountService->expects($this->once()) + ->method('find') + ->with('delegatee', 1) + ->willThrowException(new ClientException('Not found')); + + $this->delegationMapper->expects($this->once()) + ->method('findAccountOwnerForDelegatedUser') + ->with(1, 'delegatee') + ->willReturn('owner'); + + $result = $this->service->resolveAccountUserId(1, 'delegatee'); + + $this->assertEquals('owner', $result); + } + + public function testResolveAccountUserIdNotFound(): void { + $this->accountService->expects($this->once()) + ->method('find') + ->with('stranger', 1) + ->willThrowException(new ClientException('Not found')); + + $this->delegationMapper->expects($this->once()) + ->method('findAccountOwnerForDelegatedUser') + ->with(1, 'stranger') + ->willThrowException(new DoesNotExistException('No delegation found')); + + $this->expectException(ClientException::class); + + $this->service->resolveAccountUserId(1, 'stranger'); + } + + public function testResolveMailboxUserId(): void { + $mailAccount = new MailAccount(); + $mailAccount->setId(1); + $mailAccount->setUserId('owner'); + + $this->mailboxMapper->expects($this->once()) + ->method('findAccountIdForMailbox') + ->with(42) + ->willReturn(1); + + $this->accountService->expects($this->once()) + ->method('find') + ->with('owner', 1) + ->willReturn(new Account($mailAccount)); + + $result = $this->service->resolveMailboxUserId(42, 'owner'); + + $this->assertEquals('owner', $result); + } + + public function testResolveMessageUserId(): void { + $mailAccount = new MailAccount(); + $mailAccount->setId(1); + $mailAccount->setUserId('owner'); + + $this->messageMapper->expects($this->once()) + ->method('findAccountIdForMessage') + ->with(99) + ->willReturn(1); + + $this->accountService->expects($this->once()) + ->method('find') + ->with('owner', 1) + ->willReturn(new Account($mailAccount)); + + $result = $this->service->resolveMessageUserId(99, 'owner'); + + $this->assertEquals('owner', $result); + } + + public function testResolveAliasUserId(): void { + $mailAccount = new MailAccount(); + $mailAccount->setId(1); + $mailAccount->setUserId('owner'); + + $this->aliasMapper->expects($this->once()) + ->method('findAccountIdForAlias') + ->with(7) + ->willReturn(1); + + $this->accountService->expects($this->once()) + ->method('find') + ->with('owner', 1) + ->willReturn(new Account($mailAccount)); + + $result = $this->service->resolveAliasUserId(7, 'owner'); + + $this->assertEquals('owner', $result); + } + + public function testResolveLocalMessageUserId(): void { + $mailAccount = new MailAccount(); + $mailAccount->setId(1); + $mailAccount->setUserId('owner'); + + $this->localMessageMapper->expects($this->once()) + ->method('findAccountIdForLocalMessage') + ->with(55) + ->willReturn(1); + + $this->accountService->expects($this->once()) + ->method('find') + ->with('owner', 1) + ->willReturn(new Account($mailAccount)); + + $result = $this->service->resolveLocalMessageUserId(55, 'owner'); + + $this->assertEquals('owner', $result); + } +} From c6b89edbcbe08ec9d9e7cadf3aa89d148728a4c2 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Fri, 15 May 2026 01:26:52 +0000 Subject: [PATCH 028/228] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- l10n/ar.js | 1 + l10n/ar.json | 1 + l10n/ast.js | 1 + l10n/ast.json | 1 + l10n/bg.js | 1 + l10n/bg.json | 1 + l10n/br.js | 1 + l10n/br.json | 1 + l10n/ca.js | 1 + l10n/ca.json | 1 + l10n/cs.js | 1 + l10n/cs.json | 1 + l10n/da.js | 1 + l10n/da.json | 1 + l10n/de.js | 18 +++++++++++++++++ l10n/de.json | 18 +++++++++++++++++ l10n/de_DE.js | 18 +++++++++++++++++ l10n/de_DE.json | 18 +++++++++++++++++ l10n/el.js | 1 + l10n/el.json | 1 + l10n/en_GB.js | 1 + l10n/en_GB.json | 1 + l10n/eo.js | 1 + l10n/eo.json | 1 + l10n/es.js | 1 + l10n/es.json | 1 + l10n/es_419.js | 1 + l10n/es_419.json | 1 + l10n/es_AR.js | 1 + l10n/es_AR.json | 1 + l10n/es_CL.js | 1 + l10n/es_CL.json | 1 + l10n/es_CO.js | 1 + l10n/es_CO.json | 1 + l10n/es_CR.js | 1 + l10n/es_CR.json | 1 + l10n/es_DO.js | 1 + l10n/es_DO.json | 1 + l10n/es_EC.js | 1 + l10n/es_EC.json | 1 + l10n/es_GT.js | 1 + l10n/es_GT.json | 1 + l10n/es_HN.js | 1 + l10n/es_HN.json | 1 + l10n/es_MX.js | 1 + l10n/es_MX.json | 1 + l10n/es_NI.js | 1 + l10n/es_NI.json | 1 + l10n/es_PA.js | 1 + l10n/es_PA.json | 1 + l10n/es_PE.js | 1 + l10n/es_PE.json | 1 + l10n/es_PR.js | 1 + l10n/es_PR.json | 1 + l10n/es_PY.js | 1 + l10n/es_PY.json | 1 + l10n/es_SV.js | 1 + l10n/es_SV.json | 1 + l10n/es_UY.js | 1 + l10n/es_UY.json | 1 + l10n/et_EE.js | 1 + l10n/et_EE.json | 1 + l10n/eu.js | 1 + l10n/eu.json | 1 + l10n/fa.js | 1 + l10n/fa.json | 1 + l10n/fi.js | 1 + l10n/fi.json | 1 + l10n/fr.js | 1 + l10n/fr.json | 1 + l10n/ga.js | 1 + l10n/ga.json | 1 + l10n/gl.js | 1 + l10n/gl.json | 1 + l10n/he.js | 1 + l10n/he.json | 1 + l10n/hr.js | 1 + l10n/hr.json | 1 + l10n/hu.js | 1 + l10n/hu.json | 1 + l10n/ia.js | 1 + l10n/ia.json | 1 + l10n/id.js | 1 + l10n/id.json | 1 + l10n/is.js | 1 + l10n/is.json | 1 + l10n/it.js | 1 + l10n/it.json | 1 + l10n/ja.js | 1 + l10n/ja.json | 1 + l10n/ka.js | 1 + l10n/ka.json | 1 + l10n/ka_GE.js | 1 + l10n/ka_GE.json | 1 + l10n/ko.js | 1 + l10n/ko.json | 1 + l10n/lo.js | 1 + l10n/lo.json | 1 + l10n/lt_LT.js | 1 + l10n/lt_LT.json | 1 + l10n/lv.js | 1 + l10n/lv.json | 1 + l10n/mk.js | 1 + l10n/mk.json | 1 + l10n/mn.js | 1 + l10n/mn.json | 1 + l10n/nb.js | 1 + l10n/nb.json | 1 + l10n/nl.js | 1 + l10n/nl.json | 1 + l10n/oc.js | 1 + l10n/oc.json | 1 + l10n/pl.js | 1 + l10n/pl.json | 1 + l10n/pt_BR.js | 1 + l10n/pt_BR.json | 1 + l10n/pt_PT.js | 1 + l10n/pt_PT.json | 1 + l10n/ro.js | 1 + l10n/ro.json | 1 + l10n/ru.js | 1 + l10n/ru.json | 1 + l10n/sc.js | 1 + l10n/sc.json | 1 + l10n/sk.js | 1 + l10n/sk.json | 1 + l10n/sl.js | 1 + l10n/sl.json | 1 + l10n/sq.js | 1 + l10n/sq.json | 1 + l10n/sr.js | 1 + l10n/sr.json | 1 + l10n/sv.js | 1 + l10n/sv.json | 1 + l10n/sw.js | 1 + l10n/sw.json | 1 + l10n/ta.js | 50 ------------------------------------------------ l10n/ta.json | 48 ---------------------------------------------- l10n/th.js | 1 + l10n/th.json | 1 + l10n/tr.js | 1 + l10n/tr.json | 1 + l10n/ug.js | 1 + l10n/ug.json | 1 + l10n/uk.js | 1 + l10n/uk.json | 1 + l10n/vi.js | 1 + l10n/vi.json | 1 + l10n/zh_CN.js | 1 + l10n/zh_CN.json | 1 + l10n/zh_HK.js | 23 ++++++++++++++++++++++ l10n/zh_HK.json | 23 ++++++++++++++++++++++ l10n/zh_TW.js | 1 + l10n/zh_TW.json | 1 + 154 files changed, 264 insertions(+), 98 deletions(-) delete mode 100644 l10n/ta.js delete mode 100644 l10n/ta.json diff --git a/l10n/ar.js b/l10n/ar.js index ff516d86c1..64976130f3 100644 --- a/l10n/ar.js +++ b/l10n/ar.js @@ -207,6 +207,7 @@ OC.L10N.register( "Expand composer" : "تمديد الناظِم composer", "Close composer" : "إغلاق الناظِم composer", "Confirm" : "تأكيد", + "Revoke" : "سحب ", "Tag: {name} deleted" : "تمّ حذف السمة: {name} ", "An error occurred, unable to delete the tag." : "حدث خطأ؛ تعذّرَ حذف السِّمَة.", "The tag will be deleted from all messages." : "سوف يتم حذف الوسم من جميع الرسائل.", diff --git a/l10n/ar.json b/l10n/ar.json index 4eb99edadc..af0f86b7fd 100644 --- a/l10n/ar.json +++ b/l10n/ar.json @@ -205,6 +205,7 @@ "Expand composer" : "تمديد الناظِم composer", "Close composer" : "إغلاق الناظِم composer", "Confirm" : "تأكيد", + "Revoke" : "سحب ", "Tag: {name} deleted" : "تمّ حذف السمة: {name} ", "An error occurred, unable to delete the tag." : "حدث خطأ؛ تعذّرَ حذف السِّمَة.", "The tag will be deleted from all messages." : "سوف يتم حذف الوسم من جميع الرسائل.", diff --git a/l10n/ast.js b/l10n/ast.js index c54fa891d9..20081d83c7 100644 --- a/l10n/ast.js +++ b/l10n/ast.js @@ -85,6 +85,7 @@ OC.L10N.register( "Expand composer" : "Espander el compositor", "Close composer" : "Zarrar el compositor", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "An error occurred, unable to delete the tag." : "Prodúxose un error, nun ye posible desaniciar la etiqueta.", "Plain text" : "Testu ensin formatu", "Rich text" : "Testu arriquecíu", diff --git a/l10n/ast.json b/l10n/ast.json index cde7c1a61a..b3799ea1d2 100644 --- a/l10n/ast.json +++ b/l10n/ast.json @@ -83,6 +83,7 @@ "Expand composer" : "Espander el compositor", "Close composer" : "Zarrar el compositor", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "An error occurred, unable to delete the tag." : "Prodúxose un error, nun ye posible desaniciar la etiqueta.", "Plain text" : "Testu ensin formatu", "Rich text" : "Testu arriquecíu", diff --git a/l10n/bg.js b/l10n/bg.js index a4bd837ee8..3eafd0b9f6 100644 --- a/l10n/bg.js +++ b/l10n/bg.js @@ -164,6 +164,7 @@ OC.L10N.register( "Choose a file to share as a link" : "Изберете файл, който да споделите като връзка", "_{count} attachment_::_{count} attachments_" : ["{count} прикачени файлове","{count} прикачени файлове"], "Confirm" : "Потвърдете", + "Revoke" : "Отнемане", "Plain text" : "Обикновен текст", "Rich text" : "Форматиран текст", "No messages in this folder" : "Няма съобщения", diff --git a/l10n/bg.json b/l10n/bg.json index c15aab380a..ebebb16e2c 100644 --- a/l10n/bg.json +++ b/l10n/bg.json @@ -162,6 +162,7 @@ "Choose a file to share as a link" : "Изберете файл, който да споделите като връзка", "_{count} attachment_::_{count} attachments_" : ["{count} прикачени файлове","{count} прикачени файлове"], "Confirm" : "Потвърдете", + "Revoke" : "Отнемане", "Plain text" : "Обикновен текст", "Rich text" : "Форматиран текст", "No messages in this folder" : "Няма съобщения", diff --git a/l10n/br.js b/l10n/br.js index 9a6e06b7f3..6745c176fc 100644 --- a/l10n/br.js +++ b/l10n/br.js @@ -29,6 +29,7 @@ OC.L10N.register( "Refresh" : "Freskaat", "Choose" : "Dibab", "Confirm" : "Kadarnañ", + "Revoke" : "Digargañ", "Unfavorite" : "Digaretañ", "Favorite" : "Pennrollañ", "Back" : "Distro", diff --git a/l10n/br.json b/l10n/br.json index 1abcf90fba..b685e5fba9 100644 --- a/l10n/br.json +++ b/l10n/br.json @@ -27,6 +27,7 @@ "Refresh" : "Freskaat", "Choose" : "Dibab", "Confirm" : "Kadarnañ", + "Revoke" : "Digargañ", "Unfavorite" : "Digaretañ", "Favorite" : "Pennrollañ", "Back" : "Distro", diff --git a/l10n/ca.js b/l10n/ca.js index 7be161b4e9..7f738c8917 100644 --- a/l10n/ca.js +++ b/l10n/ca.js @@ -206,6 +206,7 @@ OC.L10N.register( "Expand composer" : "Ampliar el compositor", "Close composer" : "Tanca finestra del compositor", "Confirm" : "Confirma", + "Revoke" : "Revoca", "Tag: {name} deleted" : "Etiqueta: {name} suprimida", "An error occurred, unable to delete the tag." : "S'ha produït un error, no s'ha pogut suprimir l'etiqueta.", "The tag will be deleted from all messages." : "L'etiqueta se suprimirà de tots els missatges.", diff --git a/l10n/ca.json b/l10n/ca.json index 8fdaae5813..dd6a9189ee 100644 --- a/l10n/ca.json +++ b/l10n/ca.json @@ -204,6 +204,7 @@ "Expand composer" : "Ampliar el compositor", "Close composer" : "Tanca finestra del compositor", "Confirm" : "Confirma", + "Revoke" : "Revoca", "Tag: {name} deleted" : "Etiqueta: {name} suprimida", "An error occurred, unable to delete the tag." : "S'ha produït un error, no s'ha pogut suprimir l'etiqueta.", "The tag will be deleted from all messages." : "L'etiqueta se suprimirà de tots els missatges.", diff --git a/l10n/cs.js b/l10n/cs.js index 726997d075..19f6dc7be7 100644 --- a/l10n/cs.js +++ b/l10n/cs.js @@ -253,6 +253,7 @@ OC.L10N.register( "Expand composer" : "Rozbalit dialog psaní", "Close composer" : "Zavřít dialog psaní", "Confirm" : "Potvrdit", + "Revoke" : "Odvolat", "Tag: {name} deleted" : "Štítek: {name} smazán", "An error occurred, unable to delete the tag." : "Došlo k chybě, štítek se nepodařilo smazat.", "The tag will be deleted from all messages." : "Štítek bude smazán ze všech zpráv.", diff --git a/l10n/cs.json b/l10n/cs.json index 469f8d5a91..e55d26b08d 100644 --- a/l10n/cs.json +++ b/l10n/cs.json @@ -251,6 +251,7 @@ "Expand composer" : "Rozbalit dialog psaní", "Close composer" : "Zavřít dialog psaní", "Confirm" : "Potvrdit", + "Revoke" : "Odvolat", "Tag: {name} deleted" : "Štítek: {name} smazán", "An error occurred, unable to delete the tag." : "Došlo k chybě, štítek se nepodařilo smazat.", "The tag will be deleted from all messages." : "Štítek bude smazán ze všech zpráv.", diff --git a/l10n/da.js b/l10n/da.js index 71aa01cdbe..5be3d62f6a 100644 --- a/l10n/da.js +++ b/l10n/da.js @@ -215,6 +215,7 @@ OC.L10N.register( "Expand composer" : "Udvid composer", "Close composer" : "Luk composer", "Confirm" : "Bekræft", + "Revoke" : "Tilbagekald", "Tag: {name} deleted" : "Tag: {name} slettet", "An error occurred, unable to delete the tag." : "En fejl opstod, kunne ikke slette tagget", "The tag will be deleted from all messages." : "Tagget bliver slettet fra alle beskeder.", diff --git a/l10n/da.json b/l10n/da.json index 672d4f47dc..262589fde3 100644 --- a/l10n/da.json +++ b/l10n/da.json @@ -213,6 +213,7 @@ "Expand composer" : "Udvid composer", "Close composer" : "Luk composer", "Confirm" : "Bekræft", + "Revoke" : "Tilbagekald", "Tag: {name} deleted" : "Tag: {name} slettet", "An error occurred, unable to delete the tag." : "En fejl opstod, kunne ikke slette tagget", "The tag will be deleted from all messages." : "Tagget bliver slettet fra alle beskeder.", diff --git a/l10n/de.js b/l10n/de.js index ebd11596ce..c9dde59a99 100644 --- a/l10n/de.js +++ b/l10n/de.js @@ -137,6 +137,7 @@ OC.L10N.register( "Mail settings" : "Mail-Einstellungen", "General" : "Allgemein", "Set as default mail app" : "Als Standard-E-Mail-App festlegen", + "{email} (delegated)" : "{email} (Delegiert)", "Add mail account" : "E-Mail-Konto hinzufügen", "Appearance" : "Aussehen", "Show all messages in thread" : "Alle Nachrichten der Unterhaltung anzeigen", @@ -255,6 +256,21 @@ OC.L10N.register( "Expand composer" : "Erstellungsbereich erweitern", "Close composer" : "Erstellungsbereich schließen", "Confirm" : "Bestätigen", + "Delegate access" : "Zugriff delegieren", + "Revoke" : "Widerrufen", + "{userId} will no longer be able to act on your behalf" : "{userId} wird nicht mehr in deinem Namen handeln können", + "Could not fetch delegates" : "Delegierte konnten nicht abgerufen werden", + "Delegated access to {userId}" : "Zugriff delegiert an {userId}", + "Could not delegate access" : "Zugriff konnte nicht delegiert werden", + "Revoked access for {userId}" : "Zugriff für {userId} widerrufen", + "Could not revoke delegation" : "Delegation konnte nicht widerrufen werden", + "Delegation" : "Delegation", + "Allow users to send, receive, and delete mail on your behalf" : "Benutzern ermöglichen, in deinem Namen E-Mails zu senden, zu empfangen und zu löschen", + "Revoke access" : "Zugriff widerrufen", + "Add delegate" : "Delegierten hinzufügen", + "Select a user" : "Einen Benutzer auswählen", + "They will be able to send, receive, and delete mail on your behalf" : "Diese können in deinem Namen E-Mails senden, empfangen und löschen", + "Revoke access?" : "Zugriff widerrufen?", "Tag: {name} deleted" : "Schlagwort: {name} gelöscht", "An error occurred, unable to delete the tag." : "Es ist ein Fehler aufgetreten, das Schlagwort konnte nicht gelöscht werden.", "The tag will be deleted from all messages." : "Das Schlagwort wird aus allen Nachrichten gelöscht.", @@ -451,8 +467,10 @@ OC.L10N.register( "Remove account" : "Konto entfernen", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "Das Konto für {email} und zwischengespeicherte E-Mail-Daten werden aus Nextcloud entfernt, jedoch nicht von deinem E-Mail-Provider.", "Remove {email}" : "{email} entfernen", + "could not delete account" : "Konto konnte nicht gelöscht werden", "Provisioned account is disabled" : "Bereitgestelltes Konto ist deaktiviert.", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "Bitte melde dich mit einem Passwort an, um dieses Konto zu aktivieren. Die aktuelle Sitzung verwendet eine passwortlose Authentifizierung, z. B. SSO oder WebAuthn.", + "Delegate account" : "Konto delegieren", "Show only subscribed folders" : "Nur abonnierte Ordner anzeigen", "Add folder" : "Ordner hinzufügen", "Folder name" : "Ordnername", diff --git a/l10n/de.json b/l10n/de.json index 924fb357f4..0b20de310c 100644 --- a/l10n/de.json +++ b/l10n/de.json @@ -135,6 +135,7 @@ "Mail settings" : "Mail-Einstellungen", "General" : "Allgemein", "Set as default mail app" : "Als Standard-E-Mail-App festlegen", + "{email} (delegated)" : "{email} (Delegiert)", "Add mail account" : "E-Mail-Konto hinzufügen", "Appearance" : "Aussehen", "Show all messages in thread" : "Alle Nachrichten der Unterhaltung anzeigen", @@ -253,6 +254,21 @@ "Expand composer" : "Erstellungsbereich erweitern", "Close composer" : "Erstellungsbereich schließen", "Confirm" : "Bestätigen", + "Delegate access" : "Zugriff delegieren", + "Revoke" : "Widerrufen", + "{userId} will no longer be able to act on your behalf" : "{userId} wird nicht mehr in deinem Namen handeln können", + "Could not fetch delegates" : "Delegierte konnten nicht abgerufen werden", + "Delegated access to {userId}" : "Zugriff delegiert an {userId}", + "Could not delegate access" : "Zugriff konnte nicht delegiert werden", + "Revoked access for {userId}" : "Zugriff für {userId} widerrufen", + "Could not revoke delegation" : "Delegation konnte nicht widerrufen werden", + "Delegation" : "Delegation", + "Allow users to send, receive, and delete mail on your behalf" : "Benutzern ermöglichen, in deinem Namen E-Mails zu senden, zu empfangen und zu löschen", + "Revoke access" : "Zugriff widerrufen", + "Add delegate" : "Delegierten hinzufügen", + "Select a user" : "Einen Benutzer auswählen", + "They will be able to send, receive, and delete mail on your behalf" : "Diese können in deinem Namen E-Mails senden, empfangen und löschen", + "Revoke access?" : "Zugriff widerrufen?", "Tag: {name} deleted" : "Schlagwort: {name} gelöscht", "An error occurred, unable to delete the tag." : "Es ist ein Fehler aufgetreten, das Schlagwort konnte nicht gelöscht werden.", "The tag will be deleted from all messages." : "Das Schlagwort wird aus allen Nachrichten gelöscht.", @@ -449,8 +465,10 @@ "Remove account" : "Konto entfernen", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "Das Konto für {email} und zwischengespeicherte E-Mail-Daten werden aus Nextcloud entfernt, jedoch nicht von deinem E-Mail-Provider.", "Remove {email}" : "{email} entfernen", + "could not delete account" : "Konto konnte nicht gelöscht werden", "Provisioned account is disabled" : "Bereitgestelltes Konto ist deaktiviert.", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "Bitte melde dich mit einem Passwort an, um dieses Konto zu aktivieren. Die aktuelle Sitzung verwendet eine passwortlose Authentifizierung, z. B. SSO oder WebAuthn.", + "Delegate account" : "Konto delegieren", "Show only subscribed folders" : "Nur abonnierte Ordner anzeigen", "Add folder" : "Ordner hinzufügen", "Folder name" : "Ordnername", diff --git a/l10n/de_DE.js b/l10n/de_DE.js index 03b277095e..56a61a1bdd 100644 --- a/l10n/de_DE.js +++ b/l10n/de_DE.js @@ -137,6 +137,7 @@ OC.L10N.register( "Mail settings" : "E-Mail-Einstellungen", "General" : "Allgemein", "Set as default mail app" : "Als Standard-E-Mail-App festlegen", + "{email} (delegated)" : "{email} (Delegiert)", "Add mail account" : "E-Mail-Konto hinzufügen", "Appearance" : "Aussehen", "Show all messages in thread" : "Alle Nachrichten der Unterhaltung anzeigen", @@ -255,6 +256,21 @@ OC.L10N.register( "Expand composer" : "Erstellungsbereich erweitern", "Close composer" : "Erstellungsbereich schließen", "Confirm" : "Bestätigen", + "Delegate access" : "Zugriff delegieren", + "Revoke" : "Widerrufen", + "{userId} will no longer be able to act on your behalf" : "{userId} wird nicht mehr in Ihrem Namen handeln können", + "Could not fetch delegates" : "Delegierte konnten nicht abgerufen werden", + "Delegated access to {userId}" : "Zugriff delegiert an {userId}", + "Could not delegate access" : "Zugriff konnte nicht delegiert werden", + "Revoked access for {userId}" : "Zugriff für {userId} widerrufen", + "Could not revoke delegation" : "Delegation konnte nicht widerrufen werden", + "Delegation" : "Delegation", + "Allow users to send, receive, and delete mail on your behalf" : "Benutzern ermöglichen, in Ihrem Namen E-Mails zu senden, zu empfangen und zu löschen", + "Revoke access" : "Zugriff widerrufen", + "Add delegate" : "Delegierten hinzufügen", + "Select a user" : "Einen Benutzer auswählen", + "They will be able to send, receive, and delete mail on your behalf" : "Diese können in Ihrem Namen E-Mails senden, empfangen und löschen", + "Revoke access?" : "Zugriff widerrufen?", "Tag: {name} deleted" : "Schlagwort: {name} gelöscht", "An error occurred, unable to delete the tag." : "Es ist ein Fehler aufgetreten, das Schlagwort konnte nicht gelöscht werden.", "The tag will be deleted from all messages." : "Das Schlagwort wird aus allen Nachrichten gelöscht.", @@ -451,8 +467,10 @@ OC.L10N.register( "Remove account" : "Konto entfernen", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "Das Konto für {email} und zwischengespeicherte E-Mail-Daten werden aus Nextcloud entfernt, jedoch nicht von Ihrem E-Mail-Provider.", "Remove {email}" : "{email} entfernen", + "could not delete account" : "Konto konnte nicht gelöscht werden", "Provisioned account is disabled" : "Bereitgestelltes Konto ist deaktiviert", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "Bitte melden Sie sich mit einem Passwort an, um dieses Konto zu aktivieren. Die aktuelle Sitzung verwendet eine passwortlose Authentifizierung, z. B. SSO oder WebAuthn.", + "Delegate account" : "Konto delegieren", "Show only subscribed folders" : "Nur abonnierte Ordner anzeigen", "Add folder" : "Ordner hinzufügen", "Folder name" : "Ordnername", diff --git a/l10n/de_DE.json b/l10n/de_DE.json index 7f09d41552..cf774af40c 100644 --- a/l10n/de_DE.json +++ b/l10n/de_DE.json @@ -135,6 +135,7 @@ "Mail settings" : "E-Mail-Einstellungen", "General" : "Allgemein", "Set as default mail app" : "Als Standard-E-Mail-App festlegen", + "{email} (delegated)" : "{email} (Delegiert)", "Add mail account" : "E-Mail-Konto hinzufügen", "Appearance" : "Aussehen", "Show all messages in thread" : "Alle Nachrichten der Unterhaltung anzeigen", @@ -253,6 +254,21 @@ "Expand composer" : "Erstellungsbereich erweitern", "Close composer" : "Erstellungsbereich schließen", "Confirm" : "Bestätigen", + "Delegate access" : "Zugriff delegieren", + "Revoke" : "Widerrufen", + "{userId} will no longer be able to act on your behalf" : "{userId} wird nicht mehr in Ihrem Namen handeln können", + "Could not fetch delegates" : "Delegierte konnten nicht abgerufen werden", + "Delegated access to {userId}" : "Zugriff delegiert an {userId}", + "Could not delegate access" : "Zugriff konnte nicht delegiert werden", + "Revoked access for {userId}" : "Zugriff für {userId} widerrufen", + "Could not revoke delegation" : "Delegation konnte nicht widerrufen werden", + "Delegation" : "Delegation", + "Allow users to send, receive, and delete mail on your behalf" : "Benutzern ermöglichen, in Ihrem Namen E-Mails zu senden, zu empfangen und zu löschen", + "Revoke access" : "Zugriff widerrufen", + "Add delegate" : "Delegierten hinzufügen", + "Select a user" : "Einen Benutzer auswählen", + "They will be able to send, receive, and delete mail on your behalf" : "Diese können in Ihrem Namen E-Mails senden, empfangen und löschen", + "Revoke access?" : "Zugriff widerrufen?", "Tag: {name} deleted" : "Schlagwort: {name} gelöscht", "An error occurred, unable to delete the tag." : "Es ist ein Fehler aufgetreten, das Schlagwort konnte nicht gelöscht werden.", "The tag will be deleted from all messages." : "Das Schlagwort wird aus allen Nachrichten gelöscht.", @@ -449,8 +465,10 @@ "Remove account" : "Konto entfernen", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "Das Konto für {email} und zwischengespeicherte E-Mail-Daten werden aus Nextcloud entfernt, jedoch nicht von Ihrem E-Mail-Provider.", "Remove {email}" : "{email} entfernen", + "could not delete account" : "Konto konnte nicht gelöscht werden", "Provisioned account is disabled" : "Bereitgestelltes Konto ist deaktiviert", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "Bitte melden Sie sich mit einem Passwort an, um dieses Konto zu aktivieren. Die aktuelle Sitzung verwendet eine passwortlose Authentifizierung, z. B. SSO oder WebAuthn.", + "Delegate account" : "Konto delegieren", "Show only subscribed folders" : "Nur abonnierte Ordner anzeigen", "Add folder" : "Ordner hinzufügen", "Folder name" : "Ordnername", diff --git a/l10n/el.js b/l10n/el.js index be9f34f2f1..5ba84d308c 100644 --- a/l10n/el.js +++ b/l10n/el.js @@ -217,6 +217,7 @@ OC.L10N.register( "Expand composer" : "Ανάπτυξη συντάκτη", "Close composer" : "Κλείσιμο συντάκτη", "Confirm" : "Επιβεβαίωση", + "Revoke" : "Ανάκληση", "Tag: {name} deleted" : "Η ετικέτα «{name)» διαγράφηκε", "An error occurred, unable to delete the tag." : "Προέκυψε σφάλμα, δεν ήταν δυνατή η διαγραφή της ετικέτας.", "The tag will be deleted from all messages." : "Η ετικέτα θα διαγραφεί από όλα τα μηνύματα.", diff --git a/l10n/el.json b/l10n/el.json index 9b8ee6e865..a6a3825c81 100644 --- a/l10n/el.json +++ b/l10n/el.json @@ -215,6 +215,7 @@ "Expand composer" : "Ανάπτυξη συντάκτη", "Close composer" : "Κλείσιμο συντάκτη", "Confirm" : "Επιβεβαίωση", + "Revoke" : "Ανάκληση", "Tag: {name} deleted" : "Η ετικέτα «{name)» διαγράφηκε", "An error occurred, unable to delete the tag." : "Προέκυψε σφάλμα, δεν ήταν δυνατή η διαγραφή της ετικέτας.", "The tag will be deleted from all messages." : "Η ετικέτα θα διαγραφεί από όλα τα μηνύματα.", diff --git a/l10n/en_GB.js b/l10n/en_GB.js index 107a7f12a6..70eae8260d 100644 --- a/l10n/en_GB.js +++ b/l10n/en_GB.js @@ -253,6 +253,7 @@ OC.L10N.register( "Expand composer" : "Expand composer", "Close composer" : "Close composer", "Confirm" : "Confirm", + "Revoke" : "Revoke", "Tag: {name} deleted" : "Tag: {name} deleted", "An error occurred, unable to delete the tag." : "An error occurred, unable to delete the tag.", "The tag will be deleted from all messages." : "The tag will be deleted from all messages.", diff --git a/l10n/en_GB.json b/l10n/en_GB.json index 326225c0b2..4f9e7a7e70 100644 --- a/l10n/en_GB.json +++ b/l10n/en_GB.json @@ -251,6 +251,7 @@ "Expand composer" : "Expand composer", "Close composer" : "Close composer", "Confirm" : "Confirm", + "Revoke" : "Revoke", "Tag: {name} deleted" : "Tag: {name} deleted", "An error occurred, unable to delete the tag." : "An error occurred, unable to delete the tag.", "The tag will be deleted from all messages." : "The tag will be deleted from all messages.", diff --git a/l10n/eo.js b/l10n/eo.js index 997ad9670c..5a451ff7d0 100644 --- a/l10n/eo.js +++ b/l10n/eo.js @@ -71,6 +71,7 @@ OC.L10N.register( "Choose" : "Elekti", "Choose a file to add as attachment" : "Elektu dosieron aldonotan kiel kunsendaĵon", "Confirm" : "Konfirmi", + "Revoke" : "Senvalidigi", "Plain text" : "Ebenaĵa teksto", "No messages in this folder" : "Neniu mesaĝo en tiu dosierujo", "Blind copy recipients only" : "Nur blindkopiaj ricevontoj", diff --git a/l10n/eo.json b/l10n/eo.json index 6a0f656ec1..2111560d83 100644 --- a/l10n/eo.json +++ b/l10n/eo.json @@ -69,6 +69,7 @@ "Choose" : "Elekti", "Choose a file to add as attachment" : "Elektu dosieron aldonotan kiel kunsendaĵon", "Confirm" : "Konfirmi", + "Revoke" : "Senvalidigi", "Plain text" : "Ebenaĵa teksto", "No messages in this folder" : "Neniu mesaĝo en tiu dosierujo", "Blind copy recipients only" : "Nur blindkopiaj ricevontoj", diff --git a/l10n/es.js b/l10n/es.js index 508de909a6..eb96be8257 100644 --- a/l10n/es.js +++ b/l10n/es.js @@ -217,6 +217,7 @@ OC.L10N.register( "Expand composer" : "Expandir compositor", "Close composer" : "Cerrar compositor", "Confirm" : "Confirmar", + "Revoke" : "Anular", "Tag: {name} deleted" : "Etiqueta: {name} eliminada", "An error occurred, unable to delete the tag." : "Ha ocurrido un error, no se ha podido borrar la etiqueta.", "The tag will be deleted from all messages." : "La etiqueta se borrará de todos los mensajes.", diff --git a/l10n/es.json b/l10n/es.json index 67a443e20b..b84f1abe7e 100644 --- a/l10n/es.json +++ b/l10n/es.json @@ -215,6 +215,7 @@ "Expand composer" : "Expandir compositor", "Close composer" : "Cerrar compositor", "Confirm" : "Confirmar", + "Revoke" : "Anular", "Tag: {name} deleted" : "Etiqueta: {name} eliminada", "An error occurred, unable to delete the tag." : "Ha ocurrido un error, no se ha podido borrar la etiqueta.", "The tag will be deleted from all messages." : "La etiqueta se borrará de todos los mensajes.", diff --git a/l10n/es_419.js b/l10n/es_419.js index b60867703d..e301043462 100644 --- a/l10n/es_419.js +++ b/l10n/es_419.js @@ -51,6 +51,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_419.json b/l10n/es_419.json index 8e4cddc675..45bfa1eb8c 100644 --- a/l10n/es_419.json +++ b/l10n/es_419.json @@ -49,6 +49,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_AR.js b/l10n/es_AR.js index c08b87cb03..0f8996cb97 100644 --- a/l10n/es_AR.js +++ b/l10n/es_AR.js @@ -49,6 +49,7 @@ OC.L10N.register( "Choose" : "Elige", "Choose a file to add as attachment" : "Seleccione el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Favorite" : "Favorito", "Read" : "Leer", "Edit tags" : "Editar etiquetas", diff --git a/l10n/es_AR.json b/l10n/es_AR.json index 0ec092aa5e..7d5ca436c3 100644 --- a/l10n/es_AR.json +++ b/l10n/es_AR.json @@ -47,6 +47,7 @@ "Choose" : "Elige", "Choose a file to add as attachment" : "Seleccione el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Favorite" : "Favorito", "Read" : "Leer", "Edit tags" : "Editar etiquetas", diff --git a/l10n/es_CL.js b/l10n/es_CL.js index bb780d053e..035515089f 100644 --- a/l10n/es_CL.js +++ b/l10n/es_CL.js @@ -52,6 +52,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "No messages in this folder" : "No hay mensajes en esta carpeta", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", diff --git a/l10n/es_CL.json b/l10n/es_CL.json index 8c1e270ca0..b422f11d57 100644 --- a/l10n/es_CL.json +++ b/l10n/es_CL.json @@ -50,6 +50,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "No messages in this folder" : "No hay mensajes en esta carpeta", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", diff --git a/l10n/es_CO.js b/l10n/es_CO.js index 773f12eda3..c41f133ba3 100644 --- a/l10n/es_CO.js +++ b/l10n/es_CO.js @@ -52,6 +52,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "No messages in this folder" : "No hay mensajes en esta carpeta", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", diff --git a/l10n/es_CO.json b/l10n/es_CO.json index 7dc3933674..a04a0288c3 100644 --- a/l10n/es_CO.json +++ b/l10n/es_CO.json @@ -50,6 +50,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "No messages in this folder" : "No hay mensajes en esta carpeta", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", diff --git a/l10n/es_CR.js b/l10n/es_CR.js index 0f87362e5e..cb27762992 100644 --- a/l10n/es_CR.js +++ b/l10n/es_CR.js @@ -52,6 +52,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "No messages in this folder" : "No hay mensajes en esta carpeta", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", diff --git a/l10n/es_CR.json b/l10n/es_CR.json index 8c98918ba9..83355c605d 100644 --- a/l10n/es_CR.json +++ b/l10n/es_CR.json @@ -50,6 +50,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "No messages in this folder" : "No hay mensajes en esta carpeta", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", diff --git a/l10n/es_DO.js b/l10n/es_DO.js index 7dabc09326..2ce7270185 100644 --- a/l10n/es_DO.js +++ b/l10n/es_DO.js @@ -52,6 +52,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "No messages in this folder" : "No hay mensajes en esta carpeta", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", diff --git a/l10n/es_DO.json b/l10n/es_DO.json index 5a7b503de0..481db589da 100644 --- a/l10n/es_DO.json +++ b/l10n/es_DO.json @@ -50,6 +50,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "No messages in this folder" : "No hay mensajes en esta carpeta", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", diff --git a/l10n/es_EC.js b/l10n/es_EC.js index 5ea0e09048..b80c8dea8f 100644 --- a/l10n/es_EC.js +++ b/l10n/es_EC.js @@ -161,6 +161,7 @@ OC.L10N.register( "Expand composer" : "Expandir redactor", "Close composer" : "Cerrar redactor", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Plain text" : "Texto sin formato", "Rich text" : "Texto enriquecido", "No messages in this folder" : "No hay mensajes en esta carpeta", diff --git a/l10n/es_EC.json b/l10n/es_EC.json index 04d18c20ed..ff168249cd 100644 --- a/l10n/es_EC.json +++ b/l10n/es_EC.json @@ -159,6 +159,7 @@ "Expand composer" : "Expandir redactor", "Close composer" : "Cerrar redactor", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Plain text" : "Texto sin formato", "Rich text" : "Texto enriquecido", "No messages in this folder" : "No hay mensajes en esta carpeta", diff --git a/l10n/es_GT.js b/l10n/es_GT.js index 7207227b6b..276994808f 100644 --- a/l10n/es_GT.js +++ b/l10n/es_GT.js @@ -99,6 +99,7 @@ OC.L10N.register( "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Choose a file to share as a link" : "Selecciona un archivo para compartir como enlace", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Plain text" : "Texto simple", "Rich text" : "Texto enriquecido", "No messages in this folder" : "No hay mensajes en esta carpeta", diff --git a/l10n/es_GT.json b/l10n/es_GT.json index f6d68bd4e0..363dcaf8b7 100644 --- a/l10n/es_GT.json +++ b/l10n/es_GT.json @@ -97,6 +97,7 @@ "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Choose a file to share as a link" : "Selecciona un archivo para compartir como enlace", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Plain text" : "Texto simple", "Rich text" : "Texto enriquecido", "No messages in this folder" : "No hay mensajes en esta carpeta", diff --git a/l10n/es_HN.js b/l10n/es_HN.js index 6b32aad0ff..200fc13d06 100644 --- a/l10n/es_HN.js +++ b/l10n/es_HN.js @@ -52,6 +52,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_HN.json b/l10n/es_HN.json index c8c8474b65..aef8970668 100644 --- a/l10n/es_HN.json +++ b/l10n/es_HN.json @@ -50,6 +50,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_MX.js b/l10n/es_MX.js index b631f07e10..3577051456 100644 --- a/l10n/es_MX.js +++ b/l10n/es_MX.js @@ -195,6 +195,7 @@ OC.L10N.register( "Expand composer" : "Expandir redactor", "Close composer" : "Cerrar redactor", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Tag: {name} deleted" : "Etiqueta: {name} eliminada", "An error occurred, unable to delete the tag." : "Ha ocurrido un error, no se ha podido eliminar la etiqueta.", "The tag will be deleted from all messages." : "La etiqueta se eliminará de todos los mensajes.", diff --git a/l10n/es_MX.json b/l10n/es_MX.json index 909b2bed0a..09f570edcf 100644 --- a/l10n/es_MX.json +++ b/l10n/es_MX.json @@ -193,6 +193,7 @@ "Expand composer" : "Expandir redactor", "Close composer" : "Cerrar redactor", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Tag: {name} deleted" : "Etiqueta: {name} eliminada", "An error occurred, unable to delete the tag." : "Ha ocurrido un error, no se ha podido eliminar la etiqueta.", "The tag will be deleted from all messages." : "La etiqueta se eliminará de todos los mensajes.", diff --git a/l10n/es_NI.js b/l10n/es_NI.js index 34bc12c0eb..677e312a29 100644 --- a/l10n/es_NI.js +++ b/l10n/es_NI.js @@ -53,6 +53,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_NI.json b/l10n/es_NI.json index 46b29b57c0..631bca99a2 100644 --- a/l10n/es_NI.json +++ b/l10n/es_NI.json @@ -51,6 +51,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_PA.js b/l10n/es_PA.js index bddb75b7e7..3c46130679 100644 --- a/l10n/es_PA.js +++ b/l10n/es_PA.js @@ -52,6 +52,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_PA.json b/l10n/es_PA.json index d97ef64cf4..536c0be2d3 100644 --- a/l10n/es_PA.json +++ b/l10n/es_PA.json @@ -50,6 +50,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_PE.js b/l10n/es_PE.js index 438e97ea3a..dd92f0aa29 100644 --- a/l10n/es_PE.js +++ b/l10n/es_PE.js @@ -52,6 +52,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_PE.json b/l10n/es_PE.json index bfeaa1e0b8..7e07d6ad55 100644 --- a/l10n/es_PE.json +++ b/l10n/es_PE.json @@ -50,6 +50,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_PR.js b/l10n/es_PR.js index bddb75b7e7..3c46130679 100644 --- a/l10n/es_PR.js +++ b/l10n/es_PR.js @@ -52,6 +52,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_PR.json b/l10n/es_PR.json index d97ef64cf4..536c0be2d3 100644 --- a/l10n/es_PR.json +++ b/l10n/es_PR.json @@ -50,6 +50,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_PY.js b/l10n/es_PY.js index bc46def838..5ff25330ad 100644 --- a/l10n/es_PY.js +++ b/l10n/es_PY.js @@ -52,6 +52,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_PY.json b/l10n/es_PY.json index 87c604d29a..6e97a51ae2 100644 --- a/l10n/es_PY.json +++ b/l10n/es_PY.json @@ -50,6 +50,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_SV.js b/l10n/es_SV.js index 43a23cf751..f9ec6fe138 100644 --- a/l10n/es_SV.js +++ b/l10n/es_SV.js @@ -52,6 +52,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "No messages in this folder" : "No hay mensajes en esta carpeta", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", diff --git a/l10n/es_SV.json b/l10n/es_SV.json index b644f16175..789b3a9763 100644 --- a/l10n/es_SV.json +++ b/l10n/es_SV.json @@ -50,6 +50,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "No messages in this folder" : "No hay mensajes en esta carpeta", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", diff --git a/l10n/es_UY.js b/l10n/es_UY.js index bdc5a2a022..836984c2ec 100644 --- a/l10n/es_UY.js +++ b/l10n/es_UY.js @@ -52,6 +52,7 @@ OC.L10N.register( "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/es_UY.json b/l10n/es_UY.json index faa72f88f2..acc4e5d0a4 100644 --- a/l10n/es_UY.json +++ b/l10n/es_UY.json @@ -50,6 +50,7 @@ "Choose" : "Seleccionar", "Choose a file to add as attachment" : "Selecciona el archivo a agregar como adjunto", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Unfavorite" : "Quitar favorito", "Favorite" : "Hacer favorito", "Read" : "Leer", diff --git a/l10n/et_EE.js b/l10n/et_EE.js index 723547590a..0176552669 100644 --- a/l10n/et_EE.js +++ b/l10n/et_EE.js @@ -248,6 +248,7 @@ OC.L10N.register( "Expand composer" : "Näita koostamisvaadet laiemana", "Close composer" : "Sulge koostamisvaade", "Confirm" : "Kinnita", + "Revoke" : "Tühista", "Tag: {name} deleted" : "Silt: {name} on kustutatud", "An error occurred, unable to delete the tag." : "Tekkis viga, silti ei õnnestu kustutada", "The tag will be deleted from all messages." : "See silt saab olema eemaldatud kõikide kirjade juurest.", diff --git a/l10n/et_EE.json b/l10n/et_EE.json index b019f6cdea..ea1154c801 100644 --- a/l10n/et_EE.json +++ b/l10n/et_EE.json @@ -246,6 +246,7 @@ "Expand composer" : "Näita koostamisvaadet laiemana", "Close composer" : "Sulge koostamisvaade", "Confirm" : "Kinnita", + "Revoke" : "Tühista", "Tag: {name} deleted" : "Silt: {name} on kustutatud", "An error occurred, unable to delete the tag." : "Tekkis viga, silti ei õnnestu kustutada", "The tag will be deleted from all messages." : "See silt saab olema eemaldatud kõikide kirjade juurest.", diff --git a/l10n/eu.js b/l10n/eu.js index f8b2537170..1d8c77eb93 100644 --- a/l10n/eu.js +++ b/l10n/eu.js @@ -199,6 +199,7 @@ OC.L10N.register( "Expand composer" : "Hedatu egilea", "Close composer" : "Itxi egilea", "Confirm" : "Berretsi", + "Revoke" : "Ezeztatu", "Tag: {name} deleted" : "Etiketa: {name} ezabatuta", "An error occurred, unable to delete the tag." : "Errore bat gertatu da, ezin izan da etiketa ezabatu.", "The tag will be deleted from all messages." : "Mezu guztietako etiketak ezabatuko dira.", diff --git a/l10n/eu.json b/l10n/eu.json index 029eb1a83d..99c437fece 100644 --- a/l10n/eu.json +++ b/l10n/eu.json @@ -197,6 +197,7 @@ "Expand composer" : "Hedatu egilea", "Close composer" : "Itxi egilea", "Confirm" : "Berretsi", + "Revoke" : "Ezeztatu", "Tag: {name} deleted" : "Etiketa: {name} ezabatuta", "An error occurred, unable to delete the tag." : "Errore bat gertatu da, ezin izan da etiketa ezabatu.", "The tag will be deleted from all messages." : "Mezu guztietako etiketak ezabatuko dira.", diff --git a/l10n/fa.js b/l10n/fa.js index 8c58e67ea7..c28c7d8f0c 100644 --- a/l10n/fa.js +++ b/l10n/fa.js @@ -173,6 +173,7 @@ OC.L10N.register( "Expand composer" : "Expand composer", "Close composer" : "Close composer", "Confirm" : "تائید", + "Revoke" : "لغو", "Plain text" : "متن ساده", "Rich text" : "متن غنی", "No messages in this folder" : "هیچ پیامی در این پوشه وجود ندارد", diff --git a/l10n/fa.json b/l10n/fa.json index 43f8b0c935..67ac3fb0df 100644 --- a/l10n/fa.json +++ b/l10n/fa.json @@ -171,6 +171,7 @@ "Expand composer" : "Expand composer", "Close composer" : "Close composer", "Confirm" : "تائید", + "Revoke" : "لغو", "Plain text" : "متن ساده", "Rich text" : "متن غنی", "No messages in this folder" : "هیچ پیامی در این پوشه وجود ندارد", diff --git a/l10n/fi.js b/l10n/fi.js index f3928be9d4..3d8a1a1fba 100644 --- a/l10n/fi.js +++ b/l10n/fi.js @@ -184,6 +184,7 @@ OC.L10N.register( "Expand composer" : "Laajenna lähetysikkunaa", "Close composer" : "Sulje lähetysikkuna", "Confirm" : "Vahvista", + "Revoke" : "Peru oikeus", "Plain text" : "Raakateksti", "Rich text" : "Rikas teksti", "No messages in this folder" : "Tässä kansiossa ei ole viestejä", diff --git a/l10n/fi.json b/l10n/fi.json index ae48043042..092d55d7e6 100644 --- a/l10n/fi.json +++ b/l10n/fi.json @@ -182,6 +182,7 @@ "Expand composer" : "Laajenna lähetysikkunaa", "Close composer" : "Sulje lähetysikkuna", "Confirm" : "Vahvista", + "Revoke" : "Peru oikeus", "Plain text" : "Raakateksti", "Rich text" : "Rikas teksti", "No messages in this folder" : "Tässä kansiossa ei ole viestejä", diff --git a/l10n/fr.js b/l10n/fr.js index 99e7d0097e..1d3ac44134 100644 --- a/l10n/fr.js +++ b/l10n/fr.js @@ -253,6 +253,7 @@ OC.L10N.register( "Expand composer" : "Déplier la fenêtre de composition", "Close composer" : "Fermer la fenêtre de composition", "Confirm" : "Confirmer", + "Revoke" : "Révoquer", "Tag: {name} deleted" : "Étiquette : {name} supprimée", "An error occurred, unable to delete the tag." : "Une erreur est survenue, impossible de supprimer l'étiquette.", "The tag will be deleted from all messages." : "L'étiquette sera supprimée de tous les messages.", diff --git a/l10n/fr.json b/l10n/fr.json index 860bf1694e..ed4c50c434 100644 --- a/l10n/fr.json +++ b/l10n/fr.json @@ -251,6 +251,7 @@ "Expand composer" : "Déplier la fenêtre de composition", "Close composer" : "Fermer la fenêtre de composition", "Confirm" : "Confirmer", + "Revoke" : "Révoquer", "Tag: {name} deleted" : "Étiquette : {name} supprimée", "An error occurred, unable to delete the tag." : "Une erreur est survenue, impossible de supprimer l'étiquette.", "The tag will be deleted from all messages." : "L'étiquette sera supprimée de tous les messages.", diff --git a/l10n/ga.js b/l10n/ga.js index a52f4d1c26..c2abee9e9e 100644 --- a/l10n/ga.js +++ b/l10n/ga.js @@ -253,6 +253,7 @@ OC.L10N.register( "Expand composer" : "Cumadóir a leathnú", "Close composer" : "Cumadóir dlúth", "Confirm" : "Deimhnigh", + "Revoke" : "Chúlghairm", "Tag: {name} deleted" : "Chlib: {name} scriosta", "An error occurred, unable to delete the tag." : "Tharla earráid, níorbh fhéidir an chlib a scriosadh.", "The tag will be deleted from all messages." : "Scriosfar an chlib ó gach teachtaireacht.", diff --git a/l10n/ga.json b/l10n/ga.json index f2c945866f..f44ac00f7b 100644 --- a/l10n/ga.json +++ b/l10n/ga.json @@ -251,6 +251,7 @@ "Expand composer" : "Cumadóir a leathnú", "Close composer" : "Cumadóir dlúth", "Confirm" : "Deimhnigh", + "Revoke" : "Chúlghairm", "Tag: {name} deleted" : "Chlib: {name} scriosta", "An error occurred, unable to delete the tag." : "Tharla earráid, níorbh fhéidir an chlib a scriosadh.", "The tag will be deleted from all messages." : "Scriosfar an chlib ó gach teachtaireacht.", diff --git a/l10n/gl.js b/l10n/gl.js index 8ee6cfeba6..e4a07bb861 100644 --- a/l10n/gl.js +++ b/l10n/gl.js @@ -253,6 +253,7 @@ OC.L10N.register( "Expand composer" : "Estender a xanela de redacción", "Close composer" : "Pechar a xanela de redacción", "Confirm" : "Confirmar", + "Revoke" : "Revogar", "Tag: {name} deleted" : "Etiqueta: {name} eliminada", "An error occurred, unable to delete the tag." : "Produciuse un erro, non é posíbel eliminar a etiqueta.", "The tag will be deleted from all messages." : "Eliminarase a etiqueta de todas as mensaxes.", diff --git a/l10n/gl.json b/l10n/gl.json index a37c637462..e4c54ef0fa 100644 --- a/l10n/gl.json +++ b/l10n/gl.json @@ -251,6 +251,7 @@ "Expand composer" : "Estender a xanela de redacción", "Close composer" : "Pechar a xanela de redacción", "Confirm" : "Confirmar", + "Revoke" : "Revogar", "Tag: {name} deleted" : "Etiqueta: {name} eliminada", "An error occurred, unable to delete the tag." : "Produciuse un erro, non é posíbel eliminar a etiqueta.", "The tag will be deleted from all messages." : "Eliminarase a etiqueta de todas as mensaxes.", diff --git a/l10n/he.js b/l10n/he.js index cbb5be3ffd..87ba755204 100644 --- a/l10n/he.js +++ b/l10n/he.js @@ -96,6 +96,7 @@ OC.L10N.register( "Choose a file to add as attachment" : "בחירת קובץ להוספה כקובץ מצורף", "Choose a file to share as a link" : "נא לבחור קובץ לשיתוף כקישור", "Confirm" : "אימות", + "Revoke" : "שלילה", "Plain text" : "טקסט פשוט", "Rich text" : "טקסט עשיר", "No messages in this folder" : "אין הודעות בתיקייה זו", diff --git a/l10n/he.json b/l10n/he.json index 8d3cc0eceb..d9662c5b4d 100644 --- a/l10n/he.json +++ b/l10n/he.json @@ -94,6 +94,7 @@ "Choose a file to add as attachment" : "בחירת קובץ להוספה כקובץ מצורף", "Choose a file to share as a link" : "נא לבחור קובץ לשיתוף כקישור", "Confirm" : "אימות", + "Revoke" : "שלילה", "Plain text" : "טקסט פשוט", "Rich text" : "טקסט עשיר", "No messages in this folder" : "אין הודעות בתיקייה זו", diff --git a/l10n/hr.js b/l10n/hr.js index 0cedbdae6e..7465ef45d7 100644 --- a/l10n/hr.js +++ b/l10n/hr.js @@ -253,6 +253,7 @@ OC.L10N.register( "Expand composer" : "Proširi sastavljač poruke", "Close composer" : "Zatvori sastavljač poruke", "Confirm" : "Potvrdi", + "Revoke" : "Opozovi", "Tag: {name} deleted" : "Oznaka: {name} je obrisana", "An error occurred, unable to delete the tag." : "Došlo je do pogreške, nije moguće izbrisati oznaku.", "The tag will be deleted from all messages." : "Oznaka će biti uklonjena sa svih poruka.", diff --git a/l10n/hr.json b/l10n/hr.json index 2469092459..0780afa071 100644 --- a/l10n/hr.json +++ b/l10n/hr.json @@ -251,6 +251,7 @@ "Expand composer" : "Proširi sastavljač poruke", "Close composer" : "Zatvori sastavljač poruke", "Confirm" : "Potvrdi", + "Revoke" : "Opozovi", "Tag: {name} deleted" : "Oznaka: {name} je obrisana", "An error occurred, unable to delete the tag." : "Došlo je do pogreške, nije moguće izbrisati oznaku.", "The tag will be deleted from all messages." : "Oznaka će biti uklonjena sa svih poruka.", diff --git a/l10n/hu.js b/l10n/hu.js index e9ddea6c49..13084923d7 100644 --- a/l10n/hu.js +++ b/l10n/hu.js @@ -177,6 +177,7 @@ OC.L10N.register( "Expand composer" : "Szerkesztő kibontása", "Close composer" : "Szerkesztő bezárása", "Confirm" : "Megerősítés", + "Revoke" : "Visszavonás", "Plain text" : "Egyszerű szöveg", "Rich text" : "Formázott szöveg", "No messages in this folder" : "Nincs üzenet a mappában!", diff --git a/l10n/hu.json b/l10n/hu.json index bb892cbc11..7c27372476 100644 --- a/l10n/hu.json +++ b/l10n/hu.json @@ -175,6 +175,7 @@ "Expand composer" : "Szerkesztő kibontása", "Close composer" : "Szerkesztő bezárása", "Confirm" : "Megerősítés", + "Revoke" : "Visszavonás", "Plain text" : "Egyszerű szöveg", "Rich text" : "Formázott szöveg", "No messages in this folder" : "Nincs üzenet a mappában!", diff --git a/l10n/ia.js b/l10n/ia.js index 9d0ee0ff3f..d559cf7f16 100644 --- a/l10n/ia.js +++ b/l10n/ia.js @@ -24,6 +24,7 @@ OC.L10N.register( "Refresh" : "Refrescar", "Choose a file to add as attachment" : "Selectiona un file pro adder como attachamento", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Favorite" : "Favorite", "Back" : "Retro", "Attendees" : "Participantes", diff --git a/l10n/ia.json b/l10n/ia.json index 15091b4b11..ed40308a58 100644 --- a/l10n/ia.json +++ b/l10n/ia.json @@ -22,6 +22,7 @@ "Refresh" : "Refrescar", "Choose a file to add as attachment" : "Selectiona un file pro adder como attachamento", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "Favorite" : "Favorite", "Back" : "Retro", "Attendees" : "Participantes", diff --git a/l10n/id.js b/l10n/id.js index 9ef29cb266..d922917375 100644 --- a/l10n/id.js +++ b/l10n/id.js @@ -252,6 +252,7 @@ OC.L10N.register( "Expand composer" : "Perluas penyusun", "Close composer" : "Tutup penyusun", "Confirm" : "Konfirmasi", + "Revoke" : "Cabut", "Tag: {name} deleted" : "Tag: {name} dihapus", "An error occurred, unable to delete the tag." : "Terjadi kesalahan, tidak dapat menghapus tag.", "The tag will be deleted from all messages." : "Tag akan dihapus dari semua pesan.", diff --git a/l10n/id.json b/l10n/id.json index f24f1e8fe6..d66089a72b 100644 --- a/l10n/id.json +++ b/l10n/id.json @@ -250,6 +250,7 @@ "Expand composer" : "Perluas penyusun", "Close composer" : "Tutup penyusun", "Confirm" : "Konfirmasi", + "Revoke" : "Cabut", "Tag: {name} deleted" : "Tag: {name} dihapus", "An error occurred, unable to delete the tag." : "Terjadi kesalahan, tidak dapat menghapus tag.", "The tag will be deleted from all messages." : "Tag akan dihapus dari semua pesan.", diff --git a/l10n/is.js b/l10n/is.js index 97aeac7632..912f970d72 100644 --- a/l10n/is.js +++ b/l10n/is.js @@ -202,6 +202,7 @@ OC.L10N.register( "Expand composer" : "Þenja ritil", "Close composer" : "Loka ritli", "Confirm" : "Staðfesta", + "Revoke" : "Afturkalla", "Tag: {name} deleted" : "Merki: {name} eytt", "An error occurred, unable to delete the tag." : "Villa kom upp, gat ekki eytt merkinu.", "The tag will be deleted from all messages." : "Merkinu verður eytt af öllum skilaboðum.", diff --git a/l10n/is.json b/l10n/is.json index 2f4884a804..45ffa9d032 100644 --- a/l10n/is.json +++ b/l10n/is.json @@ -200,6 +200,7 @@ "Expand composer" : "Þenja ritil", "Close composer" : "Loka ritli", "Confirm" : "Staðfesta", + "Revoke" : "Afturkalla", "Tag: {name} deleted" : "Merki: {name} eytt", "An error occurred, unable to delete the tag." : "Villa kom upp, gat ekki eytt merkinu.", "The tag will be deleted from all messages." : "Merkinu verður eytt af öllum skilaboðum.", diff --git a/l10n/it.js b/l10n/it.js index a0ba68f0e8..3c3072059d 100644 --- a/l10n/it.js +++ b/l10n/it.js @@ -212,6 +212,7 @@ OC.L10N.register( "Expand composer" : "Espandi compositore", "Close composer" : "Chiudi compositore", "Confirm" : "Conferma", + "Revoke" : "Revoca", "Plain text" : "Testo semplice", "Rich text" : "Testo formattato", "No messages in this folder" : "Nessun messaggio in questa cartella", diff --git a/l10n/it.json b/l10n/it.json index d7b723f622..5b2ae3c409 100644 --- a/l10n/it.json +++ b/l10n/it.json @@ -210,6 +210,7 @@ "Expand composer" : "Espandi compositore", "Close composer" : "Chiudi compositore", "Confirm" : "Conferma", + "Revoke" : "Revoca", "Plain text" : "Testo semplice", "Rich text" : "Testo formattato", "No messages in this folder" : "Nessun messaggio in questa cartella", diff --git a/l10n/ja.js b/l10n/ja.js index 870996d036..fb1bc85104 100644 --- a/l10n/ja.js +++ b/l10n/ja.js @@ -217,6 +217,7 @@ OC.L10N.register( "Expand composer" : "コンポーザーを展開する", "Close composer" : "コンポーザーを閉じる", "Confirm" : "承認", + "Revoke" : "取り消す", "Tag: {name} deleted" : "タグ:{name}削除済み", "An error occurred, unable to delete the tag." : "タグ削除を無効にする時エラーが発生しました", "The tag will be deleted from all messages." : "全てのメッセージからタグが削除されました", diff --git a/l10n/ja.json b/l10n/ja.json index 8ca60a8a13..988a442ca5 100644 --- a/l10n/ja.json +++ b/l10n/ja.json @@ -215,6 +215,7 @@ "Expand composer" : "コンポーザーを展開する", "Close composer" : "コンポーザーを閉じる", "Confirm" : "承認", + "Revoke" : "取り消す", "Tag: {name} deleted" : "タグ:{name}削除済み", "An error occurred, unable to delete the tag." : "タグ削除を無効にする時エラーが発生しました", "The tag will be deleted from all messages." : "全てのメッセージからタグが削除されました", diff --git a/l10n/ka.js b/l10n/ka.js index 9c5958bb83..73e28b32a9 100644 --- a/l10n/ka.js +++ b/l10n/ka.js @@ -174,6 +174,7 @@ OC.L10N.register( "Expand composer" : "Expand composer", "Close composer" : "Close composer", "Confirm" : "Confirm", + "Revoke" : "Revoke", "Tag: {name} deleted" : "Tag: {name} deleted", "An error occurred, unable to delete the tag." : "An error occurred, unable to delete the tag.", "The tag will be deleted from all messages." : "The tag will be deleted from all messages.", diff --git a/l10n/ka.json b/l10n/ka.json index 325a27f288..d8ab5fbf15 100644 --- a/l10n/ka.json +++ b/l10n/ka.json @@ -172,6 +172,7 @@ "Expand composer" : "Expand composer", "Close composer" : "Close composer", "Confirm" : "Confirm", + "Revoke" : "Revoke", "Tag: {name} deleted" : "Tag: {name} deleted", "An error occurred, unable to delete the tag." : "An error occurred, unable to delete the tag.", "The tag will be deleted from all messages." : "The tag will be deleted from all messages.", diff --git a/l10n/ka_GE.js b/l10n/ka_GE.js index 33ce784970..961c83b20b 100644 --- a/l10n/ka_GE.js +++ b/l10n/ka_GE.js @@ -52,6 +52,7 @@ OC.L10N.register( "Choose" : "აირჩიეთ", "Choose a file to add as attachment" : "დანართად დასამატებლად აირჩიეთ ფაილი", "Confirm" : "დადასტურება", + "Revoke" : "წვდომის გაუქმება", "Unfavorite" : "რჩეულებიდან ამოშლა", "Favorite" : "რჩეული", "Read" : "წაკითხვა", diff --git a/l10n/ka_GE.json b/l10n/ka_GE.json index c506755e2c..735ae20af1 100644 --- a/l10n/ka_GE.json +++ b/l10n/ka_GE.json @@ -50,6 +50,7 @@ "Choose" : "აირჩიეთ", "Choose a file to add as attachment" : "დანართად დასამატებლად აირჩიეთ ფაილი", "Confirm" : "დადასტურება", + "Revoke" : "წვდომის გაუქმება", "Unfavorite" : "რჩეულებიდან ამოშლა", "Favorite" : "რჩეული", "Read" : "წაკითხვა", diff --git a/l10n/ko.js b/l10n/ko.js index f3d0b4421c..c745567553 100644 --- a/l10n/ko.js +++ b/l10n/ko.js @@ -179,6 +179,7 @@ OC.L10N.register( "_{count} attachment_::_{count} attachments_" : ["{count}개의 첨부파일"], "Untitled message" : "제목 없는 메시지", "Confirm" : "확인", + "Revoke" : "취소", "Tag: {name} deleted" : "태그: {name}이(가) 삭제됨", "An error occurred, unable to delete the tag." : "오류가 발생하여 태그를 삭제할 수 없습니다", "The tag will be deleted from all messages." : "태그가 모든 메시지로부터 삭제됩니다.", diff --git a/l10n/ko.json b/l10n/ko.json index 6ee8c76908..bd4f89f573 100644 --- a/l10n/ko.json +++ b/l10n/ko.json @@ -177,6 +177,7 @@ "_{count} attachment_::_{count} attachments_" : ["{count}개의 첨부파일"], "Untitled message" : "제목 없는 메시지", "Confirm" : "확인", + "Revoke" : "취소", "Tag: {name} deleted" : "태그: {name}이(가) 삭제됨", "An error occurred, unable to delete the tag." : "오류가 발생하여 태그를 삭제할 수 없습니다", "The tag will be deleted from all messages." : "태그가 모든 메시지로부터 삭제됩니다.", diff --git a/l10n/lo.js b/l10n/lo.js index 6928c32989..3ce045b212 100644 --- a/l10n/lo.js +++ b/l10n/lo.js @@ -240,6 +240,7 @@ OC.L10N.register( "Expand composer" : "Expand composer", "Close composer" : "Close composer", "Confirm" : "ຢືນຢັນ", + "Revoke" : "Revoke", "Tag: {name} deleted" : "Tag: {name} deleted", "An error occurred, unable to delete the tag." : "An error occurred, unable to delete the tag.", "The tag will be deleted from all messages." : "The tag will be deleted from all messages.", diff --git a/l10n/lo.json b/l10n/lo.json index f34f2671d5..daa0c79779 100644 --- a/l10n/lo.json +++ b/l10n/lo.json @@ -238,6 +238,7 @@ "Expand composer" : "Expand composer", "Close composer" : "Close composer", "Confirm" : "ຢືນຢັນ", + "Revoke" : "Revoke", "Tag: {name} deleted" : "Tag: {name} deleted", "An error occurred, unable to delete the tag." : "An error occurred, unable to delete the tag.", "The tag will be deleted from all messages." : "The tag will be deleted from all messages.", diff --git a/l10n/lt_LT.js b/l10n/lt_LT.js index f70561ef22..16860f5b3c 100644 --- a/l10n/lt_LT.js +++ b/l10n/lt_LT.js @@ -253,6 +253,7 @@ OC.L10N.register( "Expand composer" : "Išskleisti rašymo langą", "Close composer" : "Uždaryti rašymo langą", "Confirm" : "Patvirtinti", + "Revoke" : "Panaikinti", "Tag: {name} deleted" : "Žyma: {name} ištrinta", "An error occurred, unable to delete the tag." : "Įvyko klaida, nepavyko ištrinti žymos.", "The tag will be deleted from all messages." : "Žymė bus ištrinta iš visų pranešimų.", diff --git a/l10n/lt_LT.json b/l10n/lt_LT.json index 9e8e4eafee..9860c7a79b 100644 --- a/l10n/lt_LT.json +++ b/l10n/lt_LT.json @@ -251,6 +251,7 @@ "Expand composer" : "Išskleisti rašymo langą", "Close composer" : "Uždaryti rašymo langą", "Confirm" : "Patvirtinti", + "Revoke" : "Panaikinti", "Tag: {name} deleted" : "Žyma: {name} ištrinta", "An error occurred, unable to delete the tag." : "Įvyko klaida, nepavyko ištrinti žymos.", "The tag will be deleted from all messages." : "Žymė bus ištrinta iš visų pranešimų.", diff --git a/l10n/lv.js b/l10n/lv.js index b7b78589b9..9a10b22237 100644 --- a/l10n/lv.js +++ b/l10n/lv.js @@ -71,6 +71,7 @@ OC.L10N.register( "Choose a file to add as attachment" : "Izvēlēties datni, ko pievienot kā pielikumu", "Choose a file to share as a link" : "Jāizvēlas datne, ko kopīgot kā saiti", "Confirm" : "Apstiprināt", + "Revoke" : "Atsaukt", "No messages in this folder" : "Šajā mapē nav neviens ziņojums", "Later today – {timeLocale}" : "Vēlāk šodien – {timeLocale}", "Set reminder for later today" : "Iestatīt atgādinājumu vēlākam šodien", diff --git a/l10n/lv.json b/l10n/lv.json index d692f5c4c5..6599903b1c 100644 --- a/l10n/lv.json +++ b/l10n/lv.json @@ -69,6 +69,7 @@ "Choose a file to add as attachment" : "Izvēlēties datni, ko pievienot kā pielikumu", "Choose a file to share as a link" : "Jāizvēlas datne, ko kopīgot kā saiti", "Confirm" : "Apstiprināt", + "Revoke" : "Atsaukt", "No messages in this folder" : "Šajā mapē nav neviens ziņojums", "Later today – {timeLocale}" : "Vēlāk šodien – {timeLocale}", "Set reminder for later today" : "Iestatīt atgādinājumu vēlākam šodien", diff --git a/l10n/mk.js b/l10n/mk.js index 1a938fcaf3..940398d309 100644 --- a/l10n/mk.js +++ b/l10n/mk.js @@ -165,6 +165,7 @@ OC.L10N.register( "_{count} attachment_::_{count} attachments_" : ["{count} прилог","{count} прилози"], "Untitled message" : "Неименувана порака", "Confirm" : "Потврди", + "Revoke" : "Одземи", "Plain text" : "Обичен текст", "Rich text" : "Богат текст", "No messages in this folder" : "Нема пораки во оваа папка", diff --git a/l10n/mk.json b/l10n/mk.json index 4f17a18059..7fb4e93431 100644 --- a/l10n/mk.json +++ b/l10n/mk.json @@ -163,6 +163,7 @@ "_{count} attachment_::_{count} attachments_" : ["{count} прилог","{count} прилози"], "Untitled message" : "Неименувана порака", "Confirm" : "Потврди", + "Revoke" : "Одземи", "Plain text" : "Обичен текст", "Rich text" : "Богат текст", "No messages in this folder" : "Нема пораки во оваа папка", diff --git a/l10n/mn.js b/l10n/mn.js index 97132e95a0..abc9f6a682 100644 --- a/l10n/mn.js +++ b/l10n/mn.js @@ -246,6 +246,7 @@ OC.L10N.register( "Expand composer" : "Бичигчийг өргөжүүлэх", "Close composer" : "Бичигчийг хаах", "Confirm" : "–ë–∞—Ç–ª–∞—Ö", + "Revoke" : "Цуцлах", "Tag: {name} deleted" : "Таг: {name} устгагдлаа", "An error occurred, unable to delete the tag." : "Алдаа гарлаа, таг устгах боломжгүй.", "The tag will be deleted from all messages." : "Таг бүх зурвасаас устгагдана.", diff --git a/l10n/mn.json b/l10n/mn.json index e07dacd6ca..624ff98d66 100644 --- a/l10n/mn.json +++ b/l10n/mn.json @@ -244,6 +244,7 @@ "Expand composer" : "Бичигчийг өргөжүүлэх", "Close composer" : "Бичигчийг хаах", "Confirm" : "–ë–∞—Ç–ª–∞—Ö", + "Revoke" : "Цуцлах", "Tag: {name} deleted" : "Таг: {name} устгагдлаа", "An error occurred, unable to delete the tag." : "Алдаа гарлаа, таг устгах боломжгүй.", "The tag will be deleted from all messages." : "Таг бүх зурвасаас устгагдана.", diff --git a/l10n/nb.js b/l10n/nb.js index 39216d26a6..3c07a2ca7d 100644 --- a/l10n/nb.js +++ b/l10n/nb.js @@ -205,6 +205,7 @@ OC.L10N.register( "Expand composer" : "Utvid skrivebehandler", "Close composer" : "Lukk skrivebehandler", "Confirm" : "Bekreft", + "Revoke" : "Avslå", "Tag: {name} deleted" : "Merke: {name} slettet", "An error occurred, unable to delete the tag." : "En feil oppstod, ikke i stand til å slette merkelappen.", "The tag will be deleted from all messages." : "Merkelappen slettes fra alle meldinger.", diff --git a/l10n/nb.json b/l10n/nb.json index b05da10c35..5c145f8bef 100644 --- a/l10n/nb.json +++ b/l10n/nb.json @@ -203,6 +203,7 @@ "Expand composer" : "Utvid skrivebehandler", "Close composer" : "Lukk skrivebehandler", "Confirm" : "Bekreft", + "Revoke" : "Avslå", "Tag: {name} deleted" : "Merke: {name} slettet", "An error occurred, unable to delete the tag." : "En feil oppstod, ikke i stand til å slette merkelappen.", "The tag will be deleted from all messages." : "Merkelappen slettes fra alle meldinger.", diff --git a/l10n/nl.js b/l10n/nl.js index 05e87c5d92..e83c9e282d 100644 --- a/l10n/nl.js +++ b/l10n/nl.js @@ -243,6 +243,7 @@ OC.L10N.register( "_{count} attachment_::_{count} attachments_" : ["{count} bijlagen","{count} bijlagen"], "Untitled message" : "Naamloos bericht", "Confirm" : "Bevestigen", + "Revoke" : "Intrekken", "Tag: {name} deleted" : "Etiket: {name} verwijderd", "An error occurred, unable to delete the tag." : "Er is een fout opgetreden, etiket kan niet verwijderd worden.", "The tag will be deleted from all messages." : "Dit etiket wordt verwijderd uit alle berichten.", diff --git a/l10n/nl.json b/l10n/nl.json index b7466e80fb..38492d5475 100644 --- a/l10n/nl.json +++ b/l10n/nl.json @@ -241,6 +241,7 @@ "_{count} attachment_::_{count} attachments_" : ["{count} bijlagen","{count} bijlagen"], "Untitled message" : "Naamloos bericht", "Confirm" : "Bevestigen", + "Revoke" : "Intrekken", "Tag: {name} deleted" : "Etiket: {name} verwijderd", "An error occurred, unable to delete the tag." : "Er is een fout opgetreden, etiket kan niet verwijderd worden.", "The tag will be deleted from all messages." : "Dit etiket wordt verwijderd uit alle berichten.", diff --git a/l10n/oc.js b/l10n/oc.js index 413ec978e2..cbebcdc326 100644 --- a/l10n/oc.js +++ b/l10n/oc.js @@ -45,6 +45,7 @@ OC.L10N.register( "Choose" : "Causir", "Choose a file to add as attachment" : "Causissètz un fichièr de jónher al messatge", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "No messages" : "Cap de messatge", "Tomorrow – {timeLocale}" : "Deman – {timeLocale}", "Favorite" : "Favorit", diff --git a/l10n/oc.json b/l10n/oc.json index 0e7e7054bf..db7388fcb7 100644 --- a/l10n/oc.json +++ b/l10n/oc.json @@ -43,6 +43,7 @@ "Choose" : "Causir", "Choose a file to add as attachment" : "Causissètz un fichièr de jónher al messatge", "Confirm" : "Confirmar", + "Revoke" : "Revocar", "No messages" : "Cap de messatge", "Tomorrow – {timeLocale}" : "Deman – {timeLocale}", "Favorite" : "Favorit", diff --git a/l10n/pl.js b/l10n/pl.js index 59136d9b49..619b5256ab 100644 --- a/l10n/pl.js +++ b/l10n/pl.js @@ -253,6 +253,7 @@ OC.L10N.register( "Expand composer" : "Rozwiń edytor wiadomości", "Close composer" : "Zamknij edytor wiadomości", "Confirm" : "Potwierdź", + "Revoke" : "Cofnij", "Tag: {name} deleted" : "Tag: {name} usunięty", "An error occurred, unable to delete the tag." : "Wystąpił błąd, nie udało się usunąć tagu.", "The tag will be deleted from all messages." : "Tag zostanie usunięty ze wszystkich wiadomości.", diff --git a/l10n/pl.json b/l10n/pl.json index d31c7a79d6..f2545f9208 100644 --- a/l10n/pl.json +++ b/l10n/pl.json @@ -251,6 +251,7 @@ "Expand composer" : "Rozwiń edytor wiadomości", "Close composer" : "Zamknij edytor wiadomości", "Confirm" : "Potwierdź", + "Revoke" : "Cofnij", "Tag: {name} deleted" : "Tag: {name} usunięty", "An error occurred, unable to delete the tag." : "Wystąpił błąd, nie udało się usunąć tagu.", "The tag will be deleted from all messages." : "Tag zostanie usunięty ze wszystkich wiadomości.", diff --git a/l10n/pt_BR.js b/l10n/pt_BR.js index ecbd7b0e43..7b93cd8330 100644 --- a/l10n/pt_BR.js +++ b/l10n/pt_BR.js @@ -255,6 +255,7 @@ OC.L10N.register( "Expand composer" : "Expandir compositor", "Close composer" : "Fechar compositor", "Confirm" : "Confirmar", + "Revoke" : "Revogar", "Tag: {name} deleted" : "Etiqueta: {name} excluída", "An error occurred, unable to delete the tag." : "Ocorreu um erro, não foi possível excluir a etiqueta.", "The tag will be deleted from all messages." : "A etiqueta será excluída de todas as mensagens.", diff --git a/l10n/pt_BR.json b/l10n/pt_BR.json index 887045288c..11c8a206ba 100644 --- a/l10n/pt_BR.json +++ b/l10n/pt_BR.json @@ -253,6 +253,7 @@ "Expand composer" : "Expandir compositor", "Close composer" : "Fechar compositor", "Confirm" : "Confirmar", + "Revoke" : "Revogar", "Tag: {name} deleted" : "Etiqueta: {name} excluída", "An error occurred, unable to delete the tag." : "Ocorreu um erro, não foi possível excluir a etiqueta.", "The tag will be deleted from all messages." : "A etiqueta será excluída de todas as mensagens.", diff --git a/l10n/pt_PT.js b/l10n/pt_PT.js index df611f97a1..4e31e59534 100644 --- a/l10n/pt_PT.js +++ b/l10n/pt_PT.js @@ -119,6 +119,7 @@ OC.L10N.register( "Choose" : "Escolher", "Choose a file to add as attachment" : "Escolha um ficheiro para adicionar como anexo", "Confirm" : "Confirmar", + "Revoke" : "Revogar", "Plain text" : "Texto simples", "No messages" : "Sem mensagens", "Could not delete message" : "Não foi possível apagar a mensagem", diff --git a/l10n/pt_PT.json b/l10n/pt_PT.json index 011241fa6b..fed189a2d7 100644 --- a/l10n/pt_PT.json +++ b/l10n/pt_PT.json @@ -117,6 +117,7 @@ "Choose" : "Escolher", "Choose a file to add as attachment" : "Escolha um ficheiro para adicionar como anexo", "Confirm" : "Confirmar", + "Revoke" : "Revogar", "Plain text" : "Texto simples", "No messages" : "Sem mensagens", "Could not delete message" : "Não foi possível apagar a mensagem", diff --git a/l10n/ro.js b/l10n/ro.js index 42778bc2cf..b7e1fe42c6 100644 --- a/l10n/ro.js +++ b/l10n/ro.js @@ -158,6 +158,7 @@ OC.L10N.register( "Expand composer" : "Expandează mesajul", "Close composer" : "Închide mesajul", "Confirm" : "Confirmă", + "Revoke" : "Revocă", "Tag: {name} deleted" : "Eticheta: {name} a fost ștearsă", "An error occurred, unable to delete the tag." : "Eticheta nu a putut fi ștearsă.", "The tag will be deleted from all messages." : "Eticheta va fi ștearsă pentru toate mesajele.", diff --git a/l10n/ro.json b/l10n/ro.json index 18f6b27a04..b44ebdb016 100644 --- a/l10n/ro.json +++ b/l10n/ro.json @@ -156,6 +156,7 @@ "Expand composer" : "Expandează mesajul", "Close composer" : "Închide mesajul", "Confirm" : "Confirmă", + "Revoke" : "Revocă", "Tag: {name} deleted" : "Eticheta: {name} a fost ștearsă", "An error occurred, unable to delete the tag." : "Eticheta nu a putut fi ștearsă.", "The tag will be deleted from all messages." : "Eticheta va fi ștearsă pentru toate mesajele.", diff --git a/l10n/ru.js b/l10n/ru.js index 31bbf68bd4..6258d9abe4 100644 --- a/l10n/ru.js +++ b/l10n/ru.js @@ -247,6 +247,7 @@ OC.L10N.register( "_{count} attachment_::_{count} attachments_" : ["{count} вложение","{count} вложения","{count} вложений","{count} вложений"], "Untitled message" : "Сообщение без названия", "Confirm" : "Подтвердить", + "Revoke" : "Отозвать", "Tag: {name} deleted" : "Тег: {name} удален", "An error occurred, unable to delete the tag." : "Произошла ошибка, не удалось удалить тег.", "The tag will be deleted from all messages." : "Тег будет удален из всех сообщений.", diff --git a/l10n/ru.json b/l10n/ru.json index 583658b1ba..7d625faf64 100644 --- a/l10n/ru.json +++ b/l10n/ru.json @@ -245,6 +245,7 @@ "_{count} attachment_::_{count} attachments_" : ["{count} вложение","{count} вложения","{count} вложений","{count} вложений"], "Untitled message" : "Сообщение без названия", "Confirm" : "Подтвердить", + "Revoke" : "Отозвать", "Tag: {name} deleted" : "Тег: {name} удален", "An error occurred, unable to delete the tag." : "Произошла ошибка, не удалось удалить тег.", "The tag will be deleted from all messages." : "Тег будет удален из всех сообщений.", diff --git a/l10n/sc.js b/l10n/sc.js index c64c6592dd..283e8b54fe 100644 --- a/l10n/sc.js +++ b/l10n/sc.js @@ -96,6 +96,7 @@ OC.L10N.register( "Choose a file to add as attachment" : "Sèbera un'archìviu de agiùnghere comente alligongiadu", "Choose a file to share as a link" : "Sèbera un'archìviu de cumpartzire comente ligòngiu", "Confirm" : "Cunfirma", + "Revoke" : "Rèvoca", "Plain text" : "Testu simple", "Rich text" : "Testu formatadu", "No messages" : "Perunu messàgiu", diff --git a/l10n/sc.json b/l10n/sc.json index 90456afcaa..b092e9731b 100644 --- a/l10n/sc.json +++ b/l10n/sc.json @@ -94,6 +94,7 @@ "Choose a file to add as attachment" : "Sèbera un'archìviu de agiùnghere comente alligongiadu", "Choose a file to share as a link" : "Sèbera un'archìviu de cumpartzire comente ligòngiu", "Confirm" : "Cunfirma", + "Revoke" : "Rèvoca", "Plain text" : "Testu simple", "Rich text" : "Testu formatadu", "No messages" : "Perunu messàgiu", diff --git a/l10n/sk.js b/l10n/sk.js index 8667dc6004..8566e50461 100644 --- a/l10n/sk.js +++ b/l10n/sk.js @@ -253,6 +253,7 @@ OC.L10N.register( "Expand composer" : "Zobraziť editor správ", "Close composer" : "Zatvoriť editor správ", "Confirm" : "Potvrdiť", + "Revoke" : "Odvolať", "Tag: {name} deleted" : "Štítok: {name} odstránený", "An error occurred, unable to delete the tag." : "Vyskytla sa chyba, nie je možné odstrániť štítok.", "The tag will be deleted from all messages." : "Štítok bude odstránený zo všetkých správ.", diff --git a/l10n/sk.json b/l10n/sk.json index 75d7e9acce..d2c98febc3 100644 --- a/l10n/sk.json +++ b/l10n/sk.json @@ -251,6 +251,7 @@ "Expand composer" : "Zobraziť editor správ", "Close composer" : "Zatvoriť editor správ", "Confirm" : "Potvrdiť", + "Revoke" : "Odvolať", "Tag: {name} deleted" : "Štítok: {name} odstránený", "An error occurred, unable to delete the tag." : "Vyskytla sa chyba, nie je možné odstrániť štítok.", "The tag will be deleted from all messages." : "Štítok bude odstránený zo všetkých správ.", diff --git a/l10n/sl.js b/l10n/sl.js index 4eaab4a2ab..37adecb804 100644 --- a/l10n/sl.js +++ b/l10n/sl.js @@ -160,6 +160,7 @@ OC.L10N.register( "_{count} attachment_::_{count} attachments_" : ["{count} priloga","{count} prilogi","{count} priloge","{count} prilog"], "Untitled message" : "Nenaslovljeno sporočilo", "Confirm" : "Potrdi", + "Revoke" : "Prekliči", "Plain text" : "Besedilno", "Rich text" : "Obogateno besedilo", "No messages in this folder" : "V tej mapi ni sporočil", diff --git a/l10n/sl.json b/l10n/sl.json index eb78dd72c2..67b39db649 100644 --- a/l10n/sl.json +++ b/l10n/sl.json @@ -158,6 +158,7 @@ "_{count} attachment_::_{count} attachments_" : ["{count} priloga","{count} prilogi","{count} priloge","{count} prilog"], "Untitled message" : "Nenaslovljeno sporočilo", "Confirm" : "Potrdi", + "Revoke" : "Prekliči", "Plain text" : "Besedilno", "Rich text" : "Obogateno besedilo", "No messages in this folder" : "V tej mapi ni sporočil", diff --git a/l10n/sq.js b/l10n/sq.js index 6386273ba4..3261e0439b 100644 --- a/l10n/sq.js +++ b/l10n/sq.js @@ -53,6 +53,7 @@ OC.L10N.register( "Choose" : "Zgjidhni", "Choose a file to add as attachment" : "Zgjidhni një kartelë që të shtohet si bashkëngjitje", "Confirm" : "Konfirmo", + "Revoke" : "Anulo", "Unfavorite" : "Jo i/e preferuar", "Favorite" : "I/E Preferuar", "Read" : "Lexoni", diff --git a/l10n/sq.json b/l10n/sq.json index eb723015ff..25492716db 100644 --- a/l10n/sq.json +++ b/l10n/sq.json @@ -51,6 +51,7 @@ "Choose" : "Zgjidhni", "Choose a file to add as attachment" : "Zgjidhni një kartelë që të shtohet si bashkëngjitje", "Confirm" : "Konfirmo", + "Revoke" : "Anulo", "Unfavorite" : "Jo i/e preferuar", "Favorite" : "I/E Preferuar", "Read" : "Lexoni", diff --git a/l10n/sr.js b/l10n/sr.js index 6e869de912..a48650a556 100644 --- a/l10n/sr.js +++ b/l10n/sr.js @@ -236,6 +236,7 @@ OC.L10N.register( "Expand composer" : "Развиј састављач", "Close composer" : "Затвори састављач", "Confirm" : "Потврди", + "Revoke" : "Повуци", "Tag: {name} deleted" : "Ознака: {name} је обрисана", "An error occurred, unable to delete the tag." : "Дошло је до грешке, не може да се обрише ознака.", "The tag will be deleted from all messages." : "Ознака ће се обрисати из свих порука.", diff --git a/l10n/sr.json b/l10n/sr.json index 932755bd40..46749abac3 100644 --- a/l10n/sr.json +++ b/l10n/sr.json @@ -234,6 +234,7 @@ "Expand composer" : "Развиј састављач", "Close composer" : "Затвори састављач", "Confirm" : "Потврди", + "Revoke" : "Повуци", "Tag: {name} deleted" : "Ознака: {name} је обрисана", "An error occurred, unable to delete the tag." : "Дошло је до грешке, не може да се обрише ознака.", "The tag will be deleted from all messages." : "Ознака ће се обрисати из свих порука.", diff --git a/l10n/sv.js b/l10n/sv.js index c7d075236d..30a878e3d1 100644 --- a/l10n/sv.js +++ b/l10n/sv.js @@ -247,6 +247,7 @@ OC.L10N.register( "_{count} attachment_::_{count} attachments_" : ["{count} bilaga","{count} bilagor"], "Untitled message" : "Namnlöst meddelande", "Confirm" : "Bekräfta", + "Revoke" : "Återkalla", "Tag: {name} deleted" : "Tagg: {name} borttagen", "An error occurred, unable to delete the tag." : "Ett fel uppstod, det gick inte att ta bort taggen.", "The tag will be deleted from all messages." : "Taggen kommer att raderas från alla meddelanden.", diff --git a/l10n/sv.json b/l10n/sv.json index 1fc2770061..d0235ba83b 100644 --- a/l10n/sv.json +++ b/l10n/sv.json @@ -245,6 +245,7 @@ "_{count} attachment_::_{count} attachments_" : ["{count} bilaga","{count} bilagor"], "Untitled message" : "Namnlöst meddelande", "Confirm" : "Bekräfta", + "Revoke" : "Återkalla", "Tag: {name} deleted" : "Tagg: {name} borttagen", "An error occurred, unable to delete the tag." : "Ett fel uppstod, det gick inte att ta bort taggen.", "The tag will be deleted from all messages." : "Taggen kommer att raderas från alla meddelanden.", diff --git a/l10n/sw.js b/l10n/sw.js index 396f345a82..d00d0a1f6d 100644 --- a/l10n/sw.js +++ b/l10n/sw.js @@ -54,6 +54,7 @@ OC.L10N.register( "Choose a file to share as a link" : "Chagua faili kushirikisha kama kiungo", "_{count} attachment_::_{count} attachments_" : ["{count} attachment","{count} attachments"], "Confirm" : "Thibitisha", + "Revoke" : "Revoke", "No messages" : "Hakuna jumbe", "Later today – {timeLocale}" : "Baadaye leo-{timeLocale}", "Set reminder for later today" : "Set reminder for later today", diff --git a/l10n/sw.json b/l10n/sw.json index ae37b698fb..a05269e413 100644 --- a/l10n/sw.json +++ b/l10n/sw.json @@ -52,6 +52,7 @@ "Choose a file to share as a link" : "Chagua faili kushirikisha kama kiungo", "_{count} attachment_::_{count} attachments_" : ["{count} attachment","{count} attachments"], "Confirm" : "Thibitisha", + "Revoke" : "Revoke", "No messages" : "Hakuna jumbe", "Later today – {timeLocale}" : "Baadaye leo-{timeLocale}", "Set reminder for later today" : "Set reminder for later today", diff --git a/l10n/ta.js b/l10n/ta.js deleted file mode 100644 index 19d5a0ebc5..0000000000 --- a/l10n/ta.js +++ /dev/null @@ -1,50 +0,0 @@ -OC.L10N.register( - "mail", - { - "Important" : "முக்கியமான ", - "Work" : "வேலை", - "Personal" : "தனிப்பட்ட", - "Save" : "சேமிக்க ", - "Connect" : "இணைக்க", - "Name" : "பெயர்", - "Password" : "கடவுச்சொல்", - "None" : "ஒன்றுமில்லை", - "Cancel" : "இரத்து செய்க", - "General" : "பொதுவான", - "Delete" : "நீக்குக", - "Search" : "Search", - "Refresh" : "மீள் ஏற்றுக", - "Ok" : "Ok", - "Choose" : "தெரிவுசெய்க ", - "Favorite" : "விருப்பமான", - "Back" : "பின்னுக்கு", - "Attendees" : "பங்கேற்பாளர்கள்", - "Description" : "விவரிப்பு", - "Create" : "உருவாக்குக", - "Select" : "Select", - "Remove" : "அகற்றுக", - "Today" : "இன்று", - "Yesterday" : "நேற்று", - "Favorites" : "விருப்பங்கள்", - "Other" : "மற்றவை", - "Translate" : "Translate", - "Move" : "Move", - "Folder name" : "கோப்புறை பெயர்", - "Saving" : "Saving", - "Rename" : "பெயர்மாற்றம்", - "Add" : "சேர்க்க", - "Close" : "மூடுக", - "Date" : "Date", - "Tags" : "சீட்டுகள்", - "To:" : "இற்கு", - "Help" : "உதவி", - "Actions" : "செயல்கள்", - "delete" : "நீக்குக", - "Edit" : "தொகுக்க", - "User" : "User", - "Host" : "ஓம்புனர்", - "Port" : "துறை ", - "Group" : "Group", - "All" : "எல்லாம்" -}, -"nplurals=2; plural=(n != 1);"); diff --git a/l10n/ta.json b/l10n/ta.json deleted file mode 100644 index 67a9493319..0000000000 --- a/l10n/ta.json +++ /dev/null @@ -1,48 +0,0 @@ -{ "translations": { - "Important" : "முக்கியமான ", - "Work" : "வேலை", - "Personal" : "தனிப்பட்ட", - "Save" : "சேமிக்க ", - "Connect" : "இணைக்க", - "Name" : "பெயர்", - "Password" : "கடவுச்சொல்", - "None" : "ஒன்றுமில்லை", - "Cancel" : "இரத்து செய்க", - "General" : "பொதுவான", - "Delete" : "நீக்குக", - "Search" : "Search", - "Refresh" : "மீள் ஏற்றுக", - "Ok" : "Ok", - "Choose" : "தெரிவுசெய்க ", - "Favorite" : "விருப்பமான", - "Back" : "பின்னுக்கு", - "Attendees" : "பங்கேற்பாளர்கள்", - "Description" : "விவரிப்பு", - "Create" : "உருவாக்குக", - "Select" : "Select", - "Remove" : "அகற்றுக", - "Today" : "இன்று", - "Yesterday" : "நேற்று", - "Favorites" : "விருப்பங்கள்", - "Other" : "மற்றவை", - "Translate" : "Translate", - "Move" : "Move", - "Folder name" : "கோப்புறை பெயர்", - "Saving" : "Saving", - "Rename" : "பெயர்மாற்றம்", - "Add" : "சேர்க்க", - "Close" : "மூடுக", - "Date" : "Date", - "Tags" : "சீட்டுகள்", - "To:" : "இற்கு", - "Help" : "உதவி", - "Actions" : "செயல்கள்", - "delete" : "நீக்குக", - "Edit" : "தொகுக்க", - "User" : "User", - "Host" : "ஓம்புனர்", - "Port" : "துறை ", - "Group" : "Group", - "All" : "எல்லாம்" -},"pluralForm" :"nplurals=2; plural=(n != 1);" -} \ No newline at end of file diff --git a/l10n/th.js b/l10n/th.js index 65d7028213..876f997602 100644 --- a/l10n/th.js +++ b/l10n/th.js @@ -35,6 +35,7 @@ OC.L10N.register( "Choose" : "เลือก", "Choose a file to add as attachment" : "เลือกไฟล์ที่ต้องการแนบ", "Confirm" : "ยืนยัน", + "Revoke" : "เพิกถอน", "Favorite" : "รายการโปรด", "Read" : "อ่าน", "Edit tags" : "แก้ไขแท็ก", diff --git a/l10n/th.json b/l10n/th.json index 5b7c7af488..cbdb299707 100644 --- a/l10n/th.json +++ b/l10n/th.json @@ -33,6 +33,7 @@ "Choose" : "เลือก", "Choose a file to add as attachment" : "เลือกไฟล์ที่ต้องการแนบ", "Confirm" : "ยืนยัน", + "Revoke" : "เพิกถอน", "Favorite" : "รายการโปรด", "Read" : "อ่าน", "Edit tags" : "แก้ไขแท็ก", diff --git a/l10n/tr.js b/l10n/tr.js index 63f78b068d..47b1741452 100644 --- a/l10n/tr.js +++ b/l10n/tr.js @@ -253,6 +253,7 @@ OC.L10N.register( "Expand composer" : "Oluşturucuyu genişlet", "Close composer" : "Oluşturucuyu kapat", "Confirm" : "Onayla", + "Revoke" : "Geçersiz kıl", "Tag: {name} deleted" : "Etiket: {name} silindi", "An error occurred, unable to delete the tag." : "Bir sorun çıktı. Etiket silinemedi.", "The tag will be deleted from all messages." : "Etiket tüm iletilerden silinecek.", diff --git a/l10n/tr.json b/l10n/tr.json index 536eb44587..b75501f7d4 100644 --- a/l10n/tr.json +++ b/l10n/tr.json @@ -251,6 +251,7 @@ "Expand composer" : "Oluşturucuyu genişlet", "Close composer" : "Oluşturucuyu kapat", "Confirm" : "Onayla", + "Revoke" : "Geçersiz kıl", "Tag: {name} deleted" : "Etiket: {name} silindi", "An error occurred, unable to delete the tag." : "Bir sorun çıktı. Etiket silinemedi.", "The tag will be deleted from all messages." : "Etiket tüm iletilerden silinecek.", diff --git a/l10n/ug.js b/l10n/ug.js index e4a57afe25..6398c1f38e 100644 --- a/l10n/ug.js +++ b/l10n/ug.js @@ -250,6 +250,7 @@ OC.L10N.register( "Expand composer" : "كومپوزىتورنى كېڭەيتىڭ", "Close composer" : "كومپوزىتورنى تاقاش", "Confirm" : "جەزملەشتۈرۈڭ", + "Revoke" : "بىكار قىلىش", "Tag: {name} deleted" : "خەتكۈچ: {name} ئۆچۈرۈلدى", "An error occurred, unable to delete the tag." : "خەتكۈچنى ئۆچۈرەلمەي خاتالىق كۆرۈلدى.", "The tag will be deleted from all messages." : "خەتكۈچ بارلىق ئۇچۇرلاردىن ئۆچۈرۈلىدۇ.", diff --git a/l10n/ug.json b/l10n/ug.json index c960abfc98..60c9af716b 100644 --- a/l10n/ug.json +++ b/l10n/ug.json @@ -248,6 +248,7 @@ "Expand composer" : "كومپوزىتورنى كېڭەيتىڭ", "Close composer" : "كومپوزىتورنى تاقاش", "Confirm" : "جەزملەشتۈرۈڭ", + "Revoke" : "بىكار قىلىش", "Tag: {name} deleted" : "خەتكۈچ: {name} ئۆچۈرۈلدى", "An error occurred, unable to delete the tag." : "خەتكۈچنى ئۆچۈرەلمەي خاتالىق كۆرۈلدى.", "The tag will be deleted from all messages." : "خەتكۈچ بارلىق ئۇچۇرلاردىن ئۆچۈرۈلىدۇ.", diff --git a/l10n/uk.js b/l10n/uk.js index 251cb848c3..6e519b6896 100644 --- a/l10n/uk.js +++ b/l10n/uk.js @@ -218,6 +218,7 @@ OC.L10N.register( "Expand composer" : "Розгорнути редактор", "Close composer" : "Закрити редактор", "Confirm" : "Підтвердити", + "Revoke" : "Відкликати", "Tag: {name} deleted" : "Мітка: {name} вилучено", "An error occurred, unable to delete the tag." : "Пимилка під час вилучення мітки.", "The tag will be deleted from all messages." : "Мітку буде вилучено зі всіх листів.", diff --git a/l10n/uk.json b/l10n/uk.json index 0d90caa8e9..3ad8e90c7c 100644 --- a/l10n/uk.json +++ b/l10n/uk.json @@ -216,6 +216,7 @@ "Expand composer" : "Розгорнути редактор", "Close composer" : "Закрити редактор", "Confirm" : "Підтвердити", + "Revoke" : "Відкликати", "Tag: {name} deleted" : "Мітка: {name} вилучено", "An error occurred, unable to delete the tag." : "Пимилка під час вилучення мітки.", "The tag will be deleted from all messages." : "Мітку буде вилучено зі всіх листів.", diff --git a/l10n/vi.js b/l10n/vi.js index 0aa249ff40..4855155bbd 100644 --- a/l10n/vi.js +++ b/l10n/vi.js @@ -86,6 +86,7 @@ OC.L10N.register( "Choose a file to add as attachment" : "Chọn một tệp để thêm dưới dạng tệp đính kèm", "Choose a file to share as a link" : "Chọn một tệp để chia sẻ dưới dạng liên kết", "Confirm" : "Xác nhận", + "Revoke" : "Thu hồi", "No messages" : "Không có tin nhắn", "Blind copy recipients only" : "Chỉ người nhận bản sao ẩn", "Later today – {timeLocale}" : "Sau này - {timeLocale}", diff --git a/l10n/vi.json b/l10n/vi.json index 2d08fbde39..0a1ce06310 100644 --- a/l10n/vi.json +++ b/l10n/vi.json @@ -84,6 +84,7 @@ "Choose a file to add as attachment" : "Chọn một tệp để thêm dưới dạng tệp đính kèm", "Choose a file to share as a link" : "Chọn một tệp để chia sẻ dưới dạng liên kết", "Confirm" : "Xác nhận", + "Revoke" : "Thu hồi", "No messages" : "Không có tin nhắn", "Blind copy recipients only" : "Chỉ người nhận bản sao ẩn", "Later today – {timeLocale}" : "Sau này - {timeLocale}", diff --git a/l10n/zh_CN.js b/l10n/zh_CN.js index 4d5a03322f..400f7d41d5 100644 --- a/l10n/zh_CN.js +++ b/l10n/zh_CN.js @@ -253,6 +253,7 @@ OC.L10N.register( "Expand composer" : "展开编辑器", "Close composer" : "关闭编辑器", "Confirm" : "确认", + "Revoke" : "撤销", "Tag: {name} deleted" : "标签:{name} 已删除", "An error occurred, unable to delete the tag." : "发生错误,无法删除标签。", "The tag will be deleted from all messages." : "将从所有邮件中删除此标签。", diff --git a/l10n/zh_CN.json b/l10n/zh_CN.json index f5c3d4e193..646b414a99 100644 --- a/l10n/zh_CN.json +++ b/l10n/zh_CN.json @@ -251,6 +251,7 @@ "Expand composer" : "展开编辑器", "Close composer" : "关闭编辑器", "Confirm" : "确认", + "Revoke" : "撤销", "Tag: {name} deleted" : "标签:{name} 已删除", "An error occurred, unable to delete the tag." : "发生错误,无法删除标签。", "The tag will be deleted from all messages." : "将从所有邮件中删除此标签。", diff --git a/l10n/zh_HK.js b/l10n/zh_HK.js index d2137e763f..60493ff6ea 100644 --- a/l10n/zh_HK.js +++ b/l10n/zh_HK.js @@ -93,6 +93,8 @@ OC.L10N.register( "SMTP Port" : "SMTP 連接埠", "SMTP User" : "SMTP 賬號", "SMTP Password" : "SMTP 密碼", + "Google requires OAuth authentication. If your Nextcloud admin has not configured Google OAuth, you can use a Google App Password instead." : "Google 需要 OAuth 驗證。若您的 Nextcloud 管理員尚未設定 Google OAuth,您可以改用 Google 應用程式密碼。", + "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "Microsoft 需要 OAuth 驗證。請聯絡您的 Nextcloud 管理員,在管理設定中設定 Microsoft OAuth。", "Account settings" : "賬號設定", "Aliases" : "别名", "Alias to S/MIME certificate mapping" : "别名到 S/MIME 證書映射", @@ -135,6 +137,7 @@ OC.L10N.register( "Mail settings" : "電郵設定", "General" : "一般", "Set as default mail app" : "設置為默認郵件應用", + "{email} (delegated)" : "{email}(已委派)", "Add mail account" : "添加電郵帳戶", "Appearance" : "外觀", "Show all messages in thread" : "顯示線中的所有訊息", @@ -253,6 +256,21 @@ OC.L10N.register( "Expand composer" : "展開編輯器", "Close composer" : "關閉編輯器", "Confirm" : "確認", + "Delegate access" : "委派存取權限", + "Revoke" : "撤銷", + "{userId} will no longer be able to act on your behalf" : "{userId} 將無法再代表你執行操作", + "Could not fetch delegates" : "無法取得委派使用者", + "Delegated access to {userId}" : "已向 {userId} 委派存取權限", + "Could not delegate access" : "無法委派存取權限", + "Revoked access for {userId}" : "已撤銷 {userId} 的存取權限", + "Could not revoke delegation" : "無法撤銷委派", + "Delegation" : "委派", + "Allow users to send, receive, and delete mail on your behalf" : "允許使用者代表你傳送、接收及刪除郵件", + "Revoke access" : "撤銷存取權限", + "Add delegate" : "新增委派使用者", + "Select a user" : "選擇使用者", + "They will be able to send, receive, and delete mail on your behalf" : "他們將能代表你傳送、接收及刪除郵件", + "Revoke access?" : "撤銷存取權限?", "Tag: {name} deleted" : "標籤:{name} 已刪除", "An error occurred, unable to delete the tag." : "發生錯誤,無法刪除標籤。", "The tag will be deleted from all messages." : "該標籤將從所有訊息刪除。", @@ -449,8 +467,10 @@ OC.L10N.register( "Remove account" : "移除賬號", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "{email} 賬號與已快取的電子郵件將會從 Nextcloud 移除,但不會從您的電子郵件提供商移除。", "Remove {email}" : "移除 {email}", + "could not delete account" : "無法刪除帳戶", "Provisioned account is disabled" : "預配帳戶已禁用", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "請使用密碼登錄以啟用此帳戶。目前會話正在使用無密碼身份驗證,例如 SSO 或 WebAuthn。", + "Delegate account" : "委派帳戶", "Show only subscribed folders" : "僅顯示已訂閱的資料夾", "Add folder" : "添加資料夾", "Folder name" : "資料夾名稱", @@ -623,6 +643,9 @@ OC.L10N.register( "Reply to sender only" : "僅回覆寄件人", "Mark as unfavorite" : "取消標記我的最愛", "Mark as favorite" : "標記為我的最愛", + "To:" : "收件者:", + "Cc:" : "副本:", + "Bcc:" : "密件副本:", "Unsubscribe via link" : "透過連結取消訂閱", "Unsubscribing will stop all messages from the mailing list {sender}" : "取消訂閱將停止來自郵寄名單 {sender} 的所有消息", "Send unsubscribe email" : "傳送取消訂閱電子郵件", diff --git a/l10n/zh_HK.json b/l10n/zh_HK.json index c14f974806..b104c58c4a 100644 --- a/l10n/zh_HK.json +++ b/l10n/zh_HK.json @@ -91,6 +91,8 @@ "SMTP Port" : "SMTP 連接埠", "SMTP User" : "SMTP 賬號", "SMTP Password" : "SMTP 密碼", + "Google requires OAuth authentication. If your Nextcloud admin has not configured Google OAuth, you can use a Google App Password instead." : "Google 需要 OAuth 驗證。若您的 Nextcloud 管理員尚未設定 Google OAuth,您可以改用 Google 應用程式密碼。", + "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "Microsoft 需要 OAuth 驗證。請聯絡您的 Nextcloud 管理員,在管理設定中設定 Microsoft OAuth。", "Account settings" : "賬號設定", "Aliases" : "别名", "Alias to S/MIME certificate mapping" : "别名到 S/MIME 證書映射", @@ -133,6 +135,7 @@ "Mail settings" : "電郵設定", "General" : "一般", "Set as default mail app" : "設置為默認郵件應用", + "{email} (delegated)" : "{email}(已委派)", "Add mail account" : "添加電郵帳戶", "Appearance" : "外觀", "Show all messages in thread" : "顯示線中的所有訊息", @@ -251,6 +254,21 @@ "Expand composer" : "展開編輯器", "Close composer" : "關閉編輯器", "Confirm" : "確認", + "Delegate access" : "委派存取權限", + "Revoke" : "撤銷", + "{userId} will no longer be able to act on your behalf" : "{userId} 將無法再代表你執行操作", + "Could not fetch delegates" : "無法取得委派使用者", + "Delegated access to {userId}" : "已向 {userId} 委派存取權限", + "Could not delegate access" : "無法委派存取權限", + "Revoked access for {userId}" : "已撤銷 {userId} 的存取權限", + "Could not revoke delegation" : "無法撤銷委派", + "Delegation" : "委派", + "Allow users to send, receive, and delete mail on your behalf" : "允許使用者代表你傳送、接收及刪除郵件", + "Revoke access" : "撤銷存取權限", + "Add delegate" : "新增委派使用者", + "Select a user" : "選擇使用者", + "They will be able to send, receive, and delete mail on your behalf" : "他們將能代表你傳送、接收及刪除郵件", + "Revoke access?" : "撤銷存取權限?", "Tag: {name} deleted" : "標籤:{name} 已刪除", "An error occurred, unable to delete the tag." : "發生錯誤,無法刪除標籤。", "The tag will be deleted from all messages." : "該標籤將從所有訊息刪除。", @@ -447,8 +465,10 @@ "Remove account" : "移除賬號", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "{email} 賬號與已快取的電子郵件將會從 Nextcloud 移除,但不會從您的電子郵件提供商移除。", "Remove {email}" : "移除 {email}", + "could not delete account" : "無法刪除帳戶", "Provisioned account is disabled" : "預配帳戶已禁用", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "請使用密碼登錄以啟用此帳戶。目前會話正在使用無密碼身份驗證,例如 SSO 或 WebAuthn。", + "Delegate account" : "委派帳戶", "Show only subscribed folders" : "僅顯示已訂閱的資料夾", "Add folder" : "添加資料夾", "Folder name" : "資料夾名稱", @@ -621,6 +641,9 @@ "Reply to sender only" : "僅回覆寄件人", "Mark as unfavorite" : "取消標記我的最愛", "Mark as favorite" : "標記為我的最愛", + "To:" : "收件者:", + "Cc:" : "副本:", + "Bcc:" : "密件副本:", "Unsubscribe via link" : "透過連結取消訂閱", "Unsubscribing will stop all messages from the mailing list {sender}" : "取消訂閱將停止來自郵寄名單 {sender} 的所有消息", "Send unsubscribe email" : "傳送取消訂閱電子郵件", diff --git a/l10n/zh_TW.js b/l10n/zh_TW.js index 079c17847a..471ed796f5 100644 --- a/l10n/zh_TW.js +++ b/l10n/zh_TW.js @@ -255,6 +255,7 @@ OC.L10N.register( "Expand composer" : "展開編輯器", "Close composer" : "關閉編輯器", "Confirm" : "確認", + "Revoke" : "撤銷", "Tag: {name} deleted" : "標籤:{name} 已刪除", "An error occurred, unable to delete the tag." : "遇到錯誤,無法刪除標籤。", "The tag will be deleted from all messages." : "該標籤將從所有訊息刪除。", diff --git a/l10n/zh_TW.json b/l10n/zh_TW.json index 2f4cda4134..2510b5cf9b 100644 --- a/l10n/zh_TW.json +++ b/l10n/zh_TW.json @@ -253,6 +253,7 @@ "Expand composer" : "展開編輯器", "Close composer" : "關閉編輯器", "Confirm" : "確認", + "Revoke" : "撤銷", "Tag: {name} deleted" : "標籤:{name} 已刪除", "An error occurred, unable to delete the tag." : "遇到錯誤,無法刪除標籤。", "The tag will be deleted from all messages." : "該標籤將從所有訊息刪除。", From 5dd4fd14cf85b420685a3e4915255d7167a40c03 Mon Sep 17 00:00:00 2001 From: Grigory V Date: Fri, 15 May 2026 08:40:32 +0200 Subject: [PATCH 029/228] feat(Agents.md): add styling instructions Signed-off-by: Grigory V --- AGENTS.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 31b5aed5c8..b91db95b1a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -142,3 +142,11 @@ fix(deps): Update package dependencies AI-assisted: OpenCode (Claude Haiku 4.5) Signed-off-by: Name ``` + +### Styling + +For all CSS colors, spacing, and dimensions, you must use the standard Nextcloud CSS variables. + +Do not leave any magic numbers. If you need more specific control over dimensions use `calc(x*var)` when necessary. + +You can find the CSS variables already in use in this repository, and the full documentation available at this link: https://docs.nextcloud.com/server/latest/developer_manual/html_css_design/css.html. From 119a96bec966268116776044c6866dc547a801ff Mon Sep 17 00:00:00 2001 From: nextcloud-command Date: Fri, 15 May 2026 07:07:58 +0000 Subject: [PATCH 030/228] fix(dns): Update public suffix list Signed-off-by: GitHub --- resources/public_suffix_list.dat | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/resources/public_suffix_list.dat b/resources/public_suffix_list.dat index d30d5c07a0..37e4479c48 100644 --- a/resources/public_suffix_list.dat +++ b/resources/public_suffix_list.dat @@ -5,8 +5,8 @@ // Please pull this list from, and only from https://publicsuffix.org/list/public_suffix_list.dat, // rather than any other VCS sites. Pulling from any other URL is not guaranteed to be supported. -// VERSION: 2026-04-30_05-52-37_UTC -// COMMIT: 66c9bda3aa96409830cb6fc4396b39eadb889274 +// VERSION: 2026-05-14_08-35-31_UTC +// COMMIT: e452c7058d6946bd76952b128c12f5ce87a5acb8 // Instructions on pulling and using this list can be found at https://publicsuffix.org/list/. @@ -6157,7 +6157,6 @@ k12.mo.us k12.ms.us k12.mt.us k12.nc.us -// k12.nd.us - Bug 1028347 - Removed at request of Travis Rosso k12.ne.us k12.nh.us k12.nj.us @@ -6239,8 +6238,6 @@ cc.mt.us lib.mt.us cc.nc.us lib.nc.us -cc.nd.us -lib.nd.us cc.ne.us lib.ne.us cc.nh.us @@ -6824,7 +6821,7 @@ org.zw // newGTLDs -// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2026-04-15T16:04:02Z +// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2026-04-30T16:18:08Z // This list is auto-generated, don't edit it manually. // aaa : American Automobile Association, Inc. // https://www.iana.org/domains/root/db/aaa.html @@ -7010,7 +7007,7 @@ anquan // https://www.iana.org/domains/root/db/anz.html anz -// aol : Yahoo Inc. +// aol : AOL Media LLC // https://www.iana.org/domains/root/db/aol.html aol @@ -12820,10 +12817,6 @@ craft.me // Submitted by Ales Krajnik realm.cz -// Crisp IM SAS : https://crisp.chat/ -// Submitted by Baptiste Jamin -on.crisp.email - // Cryptonomic : https://cryptonomic.net/ // Submitted by Andrew Cady *.cryptonomic.net @@ -14065,6 +14058,10 @@ ltd.ng ngo.ng plc.ng +// Hostinger : https://hostinger.com +// Submitted by Valentinas Cirba +hstgr.cloud + // HostyHosting : https://hostyhosting.com hostyhosting.io @@ -16009,6 +16006,11 @@ reservd.testing.thingdust.io // Submitted by Christian Franke tickets.io +// Tigris Data, Inc. : https://www.tigrisdata.com +// Submitted by Bo Cao +t3.storage.dev +t3.storageapi.dev + // Tlon.io : https://tlon.io // Submitted by Mark Staarink arvo.network @@ -16355,6 +16357,8 @@ zeabur.app // Zerops : https://zerops.io/ // Submitted by Zerops Team *.zerops.app +prg1-zerops.zone +*.zerops.zone // Zine EOOD : https://zine.bg/ // Submitted by Martin Angelov From 0eaad6fb1f1408d1b17a3a3ceca27583f5b5fac2 Mon Sep 17 00:00:00 2001 From: greta Date: Mon, 19 Jan 2026 11:05:12 +0100 Subject: [PATCH 031/228] feat: add html and source editing support AI-assisted: OpenCode (gpt-5.4) AI-assisted: Claude Code (Claude Sonnet 4.6) Signed-off-by: greta Signed-off-by: Daniel Kesselberg --- src/components/SignatureSettings.vue | 4 +- src/components/TextEditor.vue | 81 ++++++++++++++++++-- src/tests/unit/components/TextEditor.spec.js | 74 ++++++++++++++++++ 3 files changed, 152 insertions(+), 7 deletions(-) diff --git a/src/components/SignatureSettings.vue b/src/components/SignatureSettings.vue index c45f74e066..e5bdc18d27 100644 --- a/src/components/SignatureSettings.vue +++ b/src/components/SignatureSettings.vue @@ -257,9 +257,9 @@ export default { .warning-large-signature { color: darkorange; } - +/* it's a bit hard to make it work without this max-width in the modal because it overlaps with the sidebar of the modal */ :deep(.ck.ck-toolbar-dropdown>.ck-dropdown__panel) { - max-width: 34vw; + max-width: 19vw; } diff --git a/src/components/TextEditor.vue b/src/components/TextEditor.vue index c7e7889760..9f970fdae1 100644 --- a/src/components/TextEditor.vue +++ b/src/components/TextEditor.vue @@ -30,7 +30,7 @@ import { Base64UploadAdapter, BlockQuote, Bold, - DecoupledEditor, + ClassicEditor, DropdownView, Essentials, FindAndReplace, @@ -46,6 +46,7 @@ import { Mention, Paragraph, RemoveFormat, + SourceEditing, Strikethrough, Subscript, Superscript, @@ -153,6 +154,7 @@ export default { RemoveFormat, Base64UploadAdapter, MailPlugin, + SourceEditing, TextDirectionPlugin, ]) toolbar.unshift(...[ @@ -177,6 +179,7 @@ export default { 'link', 'removeFormat', 'findAndReplace', + 'sourceEditing', ]) } @@ -185,8 +188,11 @@ export default { emojiTribute: null, contactMentionQuery: null, textSmiles: [], + sourceEditingInputHandler: null, + sourceEditingModeHandler: null, + sourceEditingDebounceTimer: null, ready: false, - editor: DecoupledEditor, + editor: ClassicEditor, config: { licenseKey: 'GPL', placeholder: this.placeholder, @@ -220,14 +226,28 @@ export default { htmlSupport: { allow: [ + 'table', + 'tbody', + 'td', + 'tfoot', + 'th', + 'thead', + 'tr', + 'figure', + { + name: 'a', + attributes: ['title', 'name', 'id'], + classes: true, + styles: true, + }, { name: 'img', - attributes: { - 'data-cid': true, - }, + attributes: ['src', 'alt', 'title', 'width', 'height', 'data-cid', 'loading'], + styles: true, }, ], }, + }, } }, @@ -236,7 +256,45 @@ export default { this.loadEditorTranslations(getLanguage()) }, + beforeDestroy() { + this.unregisterSourceEditingInputListener() + + if (this.editorInstance?.plugins.has('SourceEditing') && this.sourceEditingModeHandler) { + this.editorInstance.plugins.get('SourceEditing').off('change:isSourceEditingMode', this.sourceEditingModeHandler) + } + }, + methods: { + registerSourceEditingInputListener() { + const textarea = this.editorInstance.ui.getEditableElement('sourceEditing:main') + + if (!textarea || this.sourceEditingInputHandler) { + return + } + + this.sourceEditingInputHandler = () => { + clearTimeout(this.sourceEditingDebounceTimer) + this.sourceEditingDebounceTimer = setTimeout(() => { + this.editorInstance.plugins.get('SourceEditing').updateEditorData() + }, 300) + } + + textarea.addEventListener('input', this.sourceEditingInputHandler) + }, + + unregisterSourceEditingInputListener() { + clearTimeout(this.sourceEditingDebounceTimer) + this.sourceEditingDebounceTimer = null + + if (!this.sourceEditingInputHandler) { + return + } + + const textarea = this.editorInstance?.ui.getEditableElement('sourceEditing:main') + textarea?.removeEventListener('input', this.sourceEditingInputHandler) + this.sourceEditingInputHandler = null + }, + getLink(text) { const results = searchProvider(text) if (results.length === 1 && !results[0].title.toLowerCase().includes(text.toLowerCase())) { @@ -496,6 +554,19 @@ export default { } }, { priority: 'high' }) + if (editor.plugins.has('SourceEditing')) { + this.sourceEditingModeHandler = (_event, _name, isSourceEditingMode) => { + if (isSourceEditingMode) { + this.registerSourceEditingInputListener() + return + } + + this.unregisterSourceEditingInputListener() + } + + editor.plugins.get('SourceEditing').on('change:isSourceEditingMode', this.sourceEditingModeHandler) + } + editor.keystrokes.set('Ctrl+Enter', (event) => { logger.debug('Detected Ctrl+Enter/Cmd+Enter', event) this.$emit('submit', editor) diff --git a/src/tests/unit/components/TextEditor.spec.js b/src/tests/unit/components/TextEditor.spec.js index 100b3bc0e7..468d6d30ba 100644 --- a/src/tests/unit/components/TextEditor.spec.js +++ b/src/tests/unit/components/TextEditor.spec.js @@ -6,6 +6,7 @@ import { createLocalVue, shallowMount } from '@vue/test-utils' import { Paragraph } from 'ckeditor5' import mitt from 'mitt' +import { vi } from 'vitest' import TextEditor from '../../../components/TextEditor.vue' import MailPlugin from '../../../ckeditor/mail/MailPlugin.js' import Nextcloud from '../../../mixins/Nextcloud.js' @@ -124,4 +125,77 @@ describe('TextEditor', () => { expect(wrapper.emitted().ready[0][0].getData()) .toEqual('

bonjour bonjour

') }) + + it('emits updated data while editing source', async () => { + vi.useFakeTimers() + + const wrapper = shallowMount(TextEditor, { + localVue, + provide: { + addToFocusTrap: vi.fn(), + }, + propsData: { + value: '

bonjour

', + html: true, + bus: mitt(), + }, + }) + + const textarea = document.createElement('textarea') + const updateEditorData = vi.fn() + const sourceEditingPlugin = { + updateEditorData, + on: vi.fn(), + off: vi.fn(), + } + const editor = { + locale: { + uiLanguageDirection: 'ltr', + }, + ui: { + view: { + toolbar: { + element: document.createElement('div'), + items: [], + }, + editable: { element: document.createElement('div') }, + }, + getEditableElement: vi.fn((name) => name === 'sourceEditing:main' ? textarea : null), + }, + commands: { + get: vi.fn(() => null), + }, + plugins: { + has: vi.fn((pluginName) => pluginName === 'SourceEditing'), + get: vi.fn(() => sourceEditingPlugin), + }, + keystrokes: { + set: vi.fn(), + }, + editing: { + view: { + focus: vi.fn(), + }, + }, + } + + wrapper.vm.$refs.toolbarContainer = document.createElement('div') + wrapper.vm.$refs.editableContainer = { appendChild: vi.fn() } + + wrapper.vm.onEditorReady(editor) + + const sourceEditingModeHandler = sourceEditingPlugin.on.mock.calls.find(([evt]) => evt === 'change:isSourceEditingMode')[1] + + sourceEditingModeHandler(null, 'isSourceEditingMode', true) + textarea.dispatchEvent(new Event('input')) + + await vi.runAllTimersAsync() + + // updateEditorData() calls editor.data.set() → model change:data → Vue wrapper emits @input. + // We assert the handoff to CKEditor's pipeline here; the Vue wrapper propagation is tested + // by @ckeditor/ckeditor5-vue2's own suite. + expect(updateEditorData).toHaveBeenCalledTimes(1) + + vi.useRealTimers() + }) }) From 3de1febbf46eb57425f8ae26766a0d5ca5358119 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Fri, 15 May 2026 19:52:16 +0200 Subject: [PATCH 032/228] ci: force-enable mail app Signed-off-by: Daniel Kesselberg --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d676d8585d..d10d7235fc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -169,7 +169,7 @@ jobs: if: ${{ matrix.php-versions == '8.6' }} run: sed -i 's/max-version="8.5"/max-version="8.6"/' nextcloud/apps/mail/appinfo/info.xml - name: Install Mail - run: php -f nextcloud/occ app:enable mail + run: php -f nextcloud/occ app:enable mail --force - name: Configure Nextcloud for testing run: | php -f nextcloud/occ config:system:set app.mail.debug --type bool --value true @@ -265,7 +265,7 @@ jobs: working-directory: nextcloud/apps/mail run: composer install - name: Install the app - run: php -f nextcloud/occ app:enable mail + run: php -f nextcloud/occ app:enable mail --force - name: Set up node ${{ env.E2E_NODE_VERSION }} uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: From 203ce3603f6abf3640dbc4856e422162a55e5531 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Sat, 16 May 2026 01:35:45 +0000 Subject: [PATCH 033/228] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- l10n/ga.js | 19 +++++++++++++++++++ l10n/ga.json | 19 +++++++++++++++++++ l10n/it.js | 34 ++++++++++++++++++++++++++++++++++ l10n/it.json | 34 ++++++++++++++++++++++++++++++++++ l10n/pt_BR.js | 19 ++++++++++++++++++- l10n/pt_BR.json | 19 ++++++++++++++++++- l10n/zh_CN.js | 21 +++++++++++++++++++++ l10n/zh_CN.json | 21 +++++++++++++++++++++ 8 files changed, 184 insertions(+), 2 deletions(-) diff --git a/l10n/ga.js b/l10n/ga.js index c2abee9e9e..0582fc15e5 100644 --- a/l10n/ga.js +++ b/l10n/ga.js @@ -93,6 +93,8 @@ OC.L10N.register( "SMTP Port" : "Port SMTP", "SMTP User" : "Úsáideoir SMTP", "SMTP Password" : "Pasfhocal SMTP", + "Google requires OAuth authentication. If your Nextcloud admin has not configured Google OAuth, you can use a Google App Password instead." : "Éilíonn Google fíordheimhniú OAuth. Mura bhfuil Google OAuth cumraithe ag do riarthóir Nextcloud, is féidir leat Pasfhocal Aipe Google a úsáid ina ionad.", + "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "Éilíonn Microsoft fíordheimhniú OAuth. Iarr ar do riarthóir Nextcloud Microsoft OAuth a chumrú sna socruithe riarthóra.", "Account settings" : "Socruithe cuntas", "Aliases" : "Ailiasanna", "Alias to S/MIME certificate mapping" : "Ailias le mapáil teastais S/MIME", @@ -135,6 +137,7 @@ OC.L10N.register( "Mail settings" : "Socruithe ríomhphoist", "General" : "Ginearálta", "Set as default mail app" : "Socraigh mar aip ríomhphoist réamhshocraithe", + "{email} (delegated)" : "{email} (tarmligthe)", "Add mail account" : "Cuir cuntas ríomhphoist leis", "Appearance" : "Dealramh", "Show all messages in thread" : "Taispeáin gach teachtaireacht sa snáithe", @@ -253,7 +256,21 @@ OC.L10N.register( "Expand composer" : "Cumadóir a leathnú", "Close composer" : "Cumadóir dlúth", "Confirm" : "Deimhnigh", + "Delegate access" : "Rochtain toscaire", "Revoke" : "Chúlghairm", + "{userId} will no longer be able to act on your behalf" : "Ní bheidh {userId} in ann gníomhú ar do shon a thuilleadh", + "Could not fetch delegates" : "Níorbh fhéidir toscairí a fháil", + "Delegated access to {userId}" : "Rochtain tarmligthe chuig {userId}", + "Could not delegate access" : "Níorbh fhéidir rochtain a tharmligean", + "Revoked access for {userId}" : "Rochtain curtha ar ceal do {userId}", + "Could not revoke delegation" : "Níorbh fhéidir an toscaireacht a chúlghairm", + "Delegation" : "Toscaireacht", + "Allow users to send, receive, and delete mail on your behalf" : "Ceadaigh d'úsáideoirí ríomhphost a sheoladh, a fháil agus a scriosadh ar do shon", + "Revoke access" : "Cealaigh rochtain", + "Add delegate" : "Cuir toscaire leis", + "Select a user" : "Roghnaigh úsáideoir", + "They will be able to send, receive, and delete mail on your behalf" : "Beidh siad in ann ríomhphost a sheoladh, a fháil agus a scriosadh ar do shon.", + "Revoke access?" : "An bhfuil rochtain á cealú agat?", "Tag: {name} deleted" : "Chlib: {name} scriosta", "An error occurred, unable to delete the tag." : "Tharla earráid, níorbh fhéidir an chlib a scriosadh.", "The tag will be deleted from all messages." : "Scriosfar an chlib ó gach teachtaireacht.", @@ -450,8 +467,10 @@ OC.L10N.register( "Remove account" : "Bain cuntas", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "Bainfear an cuntas le haghaidh {email} agus na sonraí ríomhphoist i dtaisce de Nextcloud, ach ní bhainfear iad de do sholáthraí ríomhphoist.", "Remove {email}" : "Bain {email}", + "could not delete account" : "níorbh fhéidir cuntas a scriosadh", "Provisioned account is disabled" : "Tá cuntas soláthraithe díchumasaithe", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "Logáil isteach le do thoil agus pasfhocal á úsáid agat chun an cuntas seo a chumasú. Tá an seisiún reatha ag baint úsáide as fíordheimhniú gan pasfhocal, e.g. SSO nó WebAuthn.", + "Delegate account" : "Cuntas toscaire", "Show only subscribed folders" : "Taispeáin fillteáin suibscríofa amháin", "Add folder" : "Cuir fillteán leis", "Folder name" : "Ainm fillteáin", diff --git a/l10n/ga.json b/l10n/ga.json index f44ac00f7b..5060b634b3 100644 --- a/l10n/ga.json +++ b/l10n/ga.json @@ -91,6 +91,8 @@ "SMTP Port" : "Port SMTP", "SMTP User" : "Úsáideoir SMTP", "SMTP Password" : "Pasfhocal SMTP", + "Google requires OAuth authentication. If your Nextcloud admin has not configured Google OAuth, you can use a Google App Password instead." : "Éilíonn Google fíordheimhniú OAuth. Mura bhfuil Google OAuth cumraithe ag do riarthóir Nextcloud, is féidir leat Pasfhocal Aipe Google a úsáid ina ionad.", + "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "Éilíonn Microsoft fíordheimhniú OAuth. Iarr ar do riarthóir Nextcloud Microsoft OAuth a chumrú sna socruithe riarthóra.", "Account settings" : "Socruithe cuntas", "Aliases" : "Ailiasanna", "Alias to S/MIME certificate mapping" : "Ailias le mapáil teastais S/MIME", @@ -133,6 +135,7 @@ "Mail settings" : "Socruithe ríomhphoist", "General" : "Ginearálta", "Set as default mail app" : "Socraigh mar aip ríomhphoist réamhshocraithe", + "{email} (delegated)" : "{email} (tarmligthe)", "Add mail account" : "Cuir cuntas ríomhphoist leis", "Appearance" : "Dealramh", "Show all messages in thread" : "Taispeáin gach teachtaireacht sa snáithe", @@ -251,7 +254,21 @@ "Expand composer" : "Cumadóir a leathnú", "Close composer" : "Cumadóir dlúth", "Confirm" : "Deimhnigh", + "Delegate access" : "Rochtain toscaire", "Revoke" : "Chúlghairm", + "{userId} will no longer be able to act on your behalf" : "Ní bheidh {userId} in ann gníomhú ar do shon a thuilleadh", + "Could not fetch delegates" : "Níorbh fhéidir toscairí a fháil", + "Delegated access to {userId}" : "Rochtain tarmligthe chuig {userId}", + "Could not delegate access" : "Níorbh fhéidir rochtain a tharmligean", + "Revoked access for {userId}" : "Rochtain curtha ar ceal do {userId}", + "Could not revoke delegation" : "Níorbh fhéidir an toscaireacht a chúlghairm", + "Delegation" : "Toscaireacht", + "Allow users to send, receive, and delete mail on your behalf" : "Ceadaigh d'úsáideoirí ríomhphost a sheoladh, a fháil agus a scriosadh ar do shon", + "Revoke access" : "Cealaigh rochtain", + "Add delegate" : "Cuir toscaire leis", + "Select a user" : "Roghnaigh úsáideoir", + "They will be able to send, receive, and delete mail on your behalf" : "Beidh siad in ann ríomhphost a sheoladh, a fháil agus a scriosadh ar do shon.", + "Revoke access?" : "An bhfuil rochtain á cealú agat?", "Tag: {name} deleted" : "Chlib: {name} scriosta", "An error occurred, unable to delete the tag." : "Tharla earráid, níorbh fhéidir an chlib a scriosadh.", "The tag will be deleted from all messages." : "Scriosfar an chlib ó gach teachtaireacht.", @@ -448,8 +465,10 @@ "Remove account" : "Bain cuntas", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "Bainfear an cuntas le haghaidh {email} agus na sonraí ríomhphoist i dtaisce de Nextcloud, ach ní bhainfear iad de do sholáthraí ríomhphoist.", "Remove {email}" : "Bain {email}", + "could not delete account" : "níorbh fhéidir cuntas a scriosadh", "Provisioned account is disabled" : "Tá cuntas soláthraithe díchumasaithe", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "Logáil isteach le do thoil agus pasfhocal á úsáid agat chun an cuntas seo a chumasú. Tá an seisiún reatha ag baint úsáide as fíordheimhniú gan pasfhocal, e.g. SSO nó WebAuthn.", + "Delegate account" : "Cuntas toscaire", "Show only subscribed folders" : "Taispeáin fillteáin suibscríofa amháin", "Add folder" : "Cuir fillteán leis", "Folder name" : "Ainm fillteáin", diff --git a/l10n/it.js b/l10n/it.js index 3c3072059d..0437fbecb3 100644 --- a/l10n/it.js +++ b/l10n/it.js @@ -21,9 +21,14 @@ OC.L10N.register( "Sender is using a custom email: %1$s instead of the sender email: %2$s" : "Il mittente sta usando un’email personalizzata: %1$s invece dell’email del mittente: %2$s", "Sent date is in the future" : "La data di invio è nel futuro", "Mail server marked this message as phishing attempt" : "Il server di posta ha contrassegnato questo messaggio come tentativo di phishing", + "Some addresses in this message are not matching the link text" : "Alcuni link presenti in questo messaggio non corrispondono all’indirizzo visualizzato", + "Reply-To email: %1$s is different from sender email: %2$s" : "L’indirizzo email di risposta: %1$s è diverso dall’indirizzo email del mittente: %2$s", + "Mail connection performance" : "Performance della connessione di posta elettronica", "Slow mail service detected (%1$s) an attempt to connect to several accounts took an average of %2$s seconds per account" : "Rilevato servizio mail lento (%1$s): un tentativo di connessione a diversi account ha richiesto in media %2$s secondi per account", + "Slow mail service detected (%1$s) an attempt to perform a mail box list operation on several accounts took an average of %2$s seconds per account" : "Rilevato servizio di posta elettronica lento (%1$s): l’operazione di enumerazione delle caselle di posta su più account ha registrato un tempo medio di %2$s secondi per account.", "Mail Transport configuration" : "Configurazione del recapito email", "The app.mail.transport setting is not set to smtp. This configuration can cause issues with modern email security measures such as SPF and DKIM because emails are sent directly from the web server, which is often not properly configured for this purpose. To address this, we have discontinued support for the mail transport. Please remove app.mail.transport from your configuration to use the SMTP transport and hide this message. A properly configured SMTP setup is required to ensure email delivery." : "La configurazione email attuale non usa SMTP. Questo può creare problemi con i controlli di sicurezza moderni, come SPF e DKIM, perché i messaggi vengono inviati direttamente dal server web, che spesso non è configurato correttamente per l’invio di email. Il metodo di invio diretto non è più supportato. Rimuovi app.mail.transport dalla configurazione per passare all’invio tramite SMTP e non visualizzare più questo avviso. Per garantire il corretto recapito delle email è necessario configurare SMTP correttamente.", + "Mail account parameters, aliases and preferences" : "Parametri dell’account di posta, alias e preferenze", "💌 A mail app for Nextcloud" : "💌 Un'applicazione di posta per Nextcloud", "**💌 A mail app for Nextcloud**\n\n- **🚀 Integration with other Nextcloud apps!** Currently Contacts, Calendar & Files – more to come.\n- **📥 Multiple mail accounts!** Personal and company account? No problem, and a nice unified inbox. Connect any IMAP account.\n- **🔒 Send & receive encrypted mails!** Using the great [Mailvelope](https://mailvelope.com) browser extension.\n- **🙈 We’re not reinventing the wheel!** Based on the great [Horde](https://www.horde.org) libraries.\n- **📬 Want to host your own mail server?** We do not have to reimplement this as you could set up [Mail-in-a-Box](https://mailinabox.email)!\n\n## Ethical AI Rating\n\n### Priority Inbox\n\nPositive:\n* The software for training and inferencing of this model is open source.\n* The model is created and trained on-premises based on the user's own data.\n* The training data is accessible to the user, making it possible to check or correct for bias or optimise the performance and CO2 usage.\n\n### Thread Summaries (opt-in)\n\n**Rating:** 🟢/🟡/🟠/🔴\n\nThe rating depends on the installed text processing backend. See [the rating overview](https://docs.nextcloud.com/server/latest/admin_manual/ai/index.html) for details.\n\nLearn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/)." : "**💌 Un'applicazione di posta elettronica per Nextcloud**\n\n- **🚀 Integrazione con altre applicazioni Nextcloud!** Attualmente Contatti, Calendario e File - ne arriveranno altre.\n- **📥 Account di posta multipli!** Account personali e aziendali? Nessun problema, e una bella casella di posta unificata. Collega qualsiasi account IMAP.\n- **🔒 Invia e ricevi email cifrate!** Utilizzando l'estensione del browser [Mailvelope](https://mailvelope.com).\n- **🙈 Non stiamo reinventando la ruota!** Basata sulle ottime librerie di [Horde](https://horde.org).\n- **📬 Vuoi ospitare il tuo server di posta?** Non dobbiamo reimplementarlo poiché puoi configurare [Mail-in-a-Box](https://mailinabox.email)!\n\n## Valutazione etica dell'IA\n\n### Posta in arrivo prioritaria\n\nPositivo:\n* Il software per l'addestramento e l'inferenza di questo modello è open source.\n* Il modello è creato e addestrato in locale basandosi sui dati dell'utente.\n* I dati di addestramento sono accessibili all'utente, rendendo possibile controllare o correggere le soglie o ottimizzare le prestazioni e l'utilizzo di CO2.\n\n### Riassunti delle discussioni (opt-in)\n\n**Valutazione:** 🟢/🟡/🟠/🔴\n\nLa valutazione dipende dal backend di elaborazione del testo installato. Vedi [la panoramica della valutazione](https://docs.nextcloud.com/server/latest/admin_manual/ai/index.html) per i dettagli.\n\nScopri di più sulla Valutazione etica dell'IA di Nextcloud [nel nostro blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/).", "Your session has expired. The page will be reloaded." : "La sessione è scaduta. La pagina sarà ricaricata.", @@ -31,6 +36,7 @@ OC.L10N.register( "Sent messages are saved in:" : "I messaggi inviati sono salvati in:", "Deleted messages are moved in:" : "I messaggi eliminati sono spostati in:", "Archived messages are moved in:" : "I messaggi archiviati sono spostati in:", + "Snoozed messages are moved in:" : "I messaggi rimandati vengono spostati in:", "Junk messages are saved in:" : "I messaggi indesiderati sono salvati in:", "Connecting" : "Connessione in corso", "Reconnect Google account" : "Ricollega l'account Google", @@ -39,17 +45,31 @@ OC.L10N.register( "Sign in with Microsoft" : "Accedi con Microsoft", "Save" : "Salva", "Connect" : "Connetti", + "Looking up configuration" : "Recupero della configurazione", "Checking mail host connectivity" : "Verifica della connettività dell'host di posta", + "Configuration discovery failed. Please use the manual settings" : "Rilevamento automatico della configurazione non riuscito. Si prega di utilizzare le impostazioni manuali.", "Password required" : "Password richiesta", + "Testing authentication" : "Verifica dell’autenticazione in corso", "Awaiting user consent" : "In attesa del consenso dell'utente.", + "Account created. Please follow the pop-up instructions to link your Google account" : "Account creato. Seguire le istruzioni del pop-up per collegare il proprio account Google", + "Account created. Please follow the pop-up instructions to link your Microsoft account" : "Account creato. Seguire le istruzioni del pop-up per collegare il proprio account Microsoft", "Loading account" : "Caricamento account", + "Account updated. Please follow the pop-up instructions to reconnect your Google account" : "Account aggiornato. Seguire le istruzioni del pop-up per riconnettere il proprio account Google", + "Account updated. Please follow the pop-up instructions to reconnect your Microsoft account" : "Account aggiornato. Seguire le istruzioni del pop-up per riconnettere il proprio account Microsoft", "Account updated" : "Account aggiornato", "IMAP server is not reachable" : "Il server IMAP non è raggiungibile", "SMTP server is not reachable" : "Il server SMTP non è raggiungibile", "IMAP username or password is wrong" : "Il nome utente o la password IMAP sono errati.", "SMTP username or password is wrong" : "Il nome utente o la password SMTP sono errati.", + "IMAP server denied authentication" : "Autenticazione negata dal server IMAP", + "SMTP server denied authentication" : "Autenticazione negata dal server SMTP", + "IMAP authentication error" : "Errore di autenticazione IMAP", + "SMTP authentication error" : "Errore di autenticazione SMTP", "IMAP connection failed" : "Connessione IMAP non riuscita", "SMTP connection failed" : "Connessione SMTP non riuscita", + "Authorization pop-up closed" : "La finestra pop-up di autorizzazione è stata chiusa", + "Configuration discovery temporarily not available. Please try again later." : "Il rilevamento della configurazione è temporaneamente non disponibile. Riprovare più tardi.", + "There was an error while setting up your account" : "Si è verificato un errore durante la configurazione dell’account.", "Auto" : "Automatico", "Name" : "Nome", "Mail address" : "Indirizzo di posta", @@ -73,6 +93,8 @@ OC.L10N.register( "SMTP Port" : "Porta SMTP", "SMTP User" : "Utente SMTP", "SMTP Password" : "Password SMTP", + "Google requires OAuth authentication. If your Nextcloud admin has not configured Google OAuth, you can use a Google App Password instead." : "Google richiede l’autenticazione OAuth. Se l’amministratore di Nextcloud non ha configurato Google OAuth, è possibile utilizzare una password per app Google.", + "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "Microsoft richiede l’autenticazione OAuth. Si prega di contattare l’amministratore di Nextcloud per configurare OAuth di Microsoft nelle impostazioni di amministrazione.", "Account settings" : "Impostazioni account", "Aliases" : "Alias", "Alias to S/MIME certificate mapping" : "Mappatura alias e certificati S/MIME", @@ -108,11 +130,13 @@ OC.L10N.register( "Cancel" : "Annulla", "Search the body of messages in priority Inbox" : "Cerca nel corpo dei messaggi nella posta prioritaria", "Activate" : "Attiva", + "Remind about messages that require a reply but received none" : "Promemoria per i messaggi che richiedono una risposta ma non hanno ricevuto alcun riscontro", "Highlight external addresses" : "Evidenzia indirizzi esterni", "Could not update preference" : "Impossibile aggiornare la preferenza", "Mail settings" : "Impostazioni Posta", "General" : "Generale", "Set as default mail app" : "Imposta come applicazione di posta predefinita", + "{email} (delegated)" : "{email} (delegata)", "Add mail account" : "Aggiungi account di posta", "Appearance" : "Aspetto", "Show all messages in thread" : "Mostra tutti i messaggi della conversazione", @@ -144,8 +168,12 @@ OC.L10N.register( "S/MIME" : "S/MIME", "Manage certificates" : "Gestisci certificati", "Mailvelope" : "Mailvelope", + "Mailvelope is enabled for the current domain." : "Mailvelope è abilitato per il dominio corrente.", + "Step 1" : "Passo 1", "Install the browser extension" : "Installa l'estensione del browser", + "Step 2" : "Passo 2", "Enable for the current domain" : "Abilita per il dominio corrente", + "Assistance features" : "Funzionalità di assistenza", "Compose new message" : "Componi nuovo messaggio", "Newer message" : "Messaggio più recente", "Older message" : "Messaggio più datato", @@ -158,13 +186,19 @@ OC.L10N.register( "Refresh" : "Aggiorna", "About" : "Informazioni", "Acknowledgements" : "Ringraziamenti", + "This application includes CKEditor, an open-source editor. Copyright © CKEditor contributors. Licensed under GPLv2." : "Questa applicazione include CKEditor, un editor open-source. Copyright © dei contributori di CKEditor. Concesso in licenza GPLv2.", + "Title of the text block" : "Titolo del blocco di testo", + "Content of the text block" : "Contenuto del blocco di testo", "Ok" : "Ok", + "Automatically create tentative appointments in calendar" : "Crea automaticamente appuntamenti provvisori nel calendario", "No certificate" : "Nessun certificato", "Certificate updated" : "Certificato aggiornato", "Could not update certificate" : "Impossibile aggiornare il certificato", "{commonName} - Valid until {expiryDate}" : "{commonName} - Valido fino al {expiryDate}", "Select an alias" : "Seleziona un alias", + "Select certificates" : "Seleziona i certificati", "Update Certificate" : "Aggiorna certificato", + "The selected certificate is not trusted by the server. Recipients might not be able to verify your signature." : "Il certificato selezionato non è considerato attendibile dal server. I destinatari potrebbero non essere in grado di verificare la tua firma.", "Encrypt with S/MIME and send later" : "Cifra con S/MIME e invia in seguito", "Encrypt with S/MIME and send" : "Cifra con S/MIME e invia", "Encrypt with Mailvelope and send later" : "Cifra con Mailvelope e invia in seguito", diff --git a/l10n/it.json b/l10n/it.json index 5b2ae3c409..bd275310c6 100644 --- a/l10n/it.json +++ b/l10n/it.json @@ -19,9 +19,14 @@ "Sender is using a custom email: %1$s instead of the sender email: %2$s" : "Il mittente sta usando un’email personalizzata: %1$s invece dell’email del mittente: %2$s", "Sent date is in the future" : "La data di invio è nel futuro", "Mail server marked this message as phishing attempt" : "Il server di posta ha contrassegnato questo messaggio come tentativo di phishing", + "Some addresses in this message are not matching the link text" : "Alcuni link presenti in questo messaggio non corrispondono all’indirizzo visualizzato", + "Reply-To email: %1$s is different from sender email: %2$s" : "L’indirizzo email di risposta: %1$s è diverso dall’indirizzo email del mittente: %2$s", + "Mail connection performance" : "Performance della connessione di posta elettronica", "Slow mail service detected (%1$s) an attempt to connect to several accounts took an average of %2$s seconds per account" : "Rilevato servizio mail lento (%1$s): un tentativo di connessione a diversi account ha richiesto in media %2$s secondi per account", + "Slow mail service detected (%1$s) an attempt to perform a mail box list operation on several accounts took an average of %2$s seconds per account" : "Rilevato servizio di posta elettronica lento (%1$s): l’operazione di enumerazione delle caselle di posta su più account ha registrato un tempo medio di %2$s secondi per account.", "Mail Transport configuration" : "Configurazione del recapito email", "The app.mail.transport setting is not set to smtp. This configuration can cause issues with modern email security measures such as SPF and DKIM because emails are sent directly from the web server, which is often not properly configured for this purpose. To address this, we have discontinued support for the mail transport. Please remove app.mail.transport from your configuration to use the SMTP transport and hide this message. A properly configured SMTP setup is required to ensure email delivery." : "La configurazione email attuale non usa SMTP. Questo può creare problemi con i controlli di sicurezza moderni, come SPF e DKIM, perché i messaggi vengono inviati direttamente dal server web, che spesso non è configurato correttamente per l’invio di email. Il metodo di invio diretto non è più supportato. Rimuovi app.mail.transport dalla configurazione per passare all’invio tramite SMTP e non visualizzare più questo avviso. Per garantire il corretto recapito delle email è necessario configurare SMTP correttamente.", + "Mail account parameters, aliases and preferences" : "Parametri dell’account di posta, alias e preferenze", "💌 A mail app for Nextcloud" : "💌 Un'applicazione di posta per Nextcloud", "**💌 A mail app for Nextcloud**\n\n- **🚀 Integration with other Nextcloud apps!** Currently Contacts, Calendar & Files – more to come.\n- **📥 Multiple mail accounts!** Personal and company account? No problem, and a nice unified inbox. Connect any IMAP account.\n- **🔒 Send & receive encrypted mails!** Using the great [Mailvelope](https://mailvelope.com) browser extension.\n- **🙈 We’re not reinventing the wheel!** Based on the great [Horde](https://www.horde.org) libraries.\n- **📬 Want to host your own mail server?** We do not have to reimplement this as you could set up [Mail-in-a-Box](https://mailinabox.email)!\n\n## Ethical AI Rating\n\n### Priority Inbox\n\nPositive:\n* The software for training and inferencing of this model is open source.\n* The model is created and trained on-premises based on the user's own data.\n* The training data is accessible to the user, making it possible to check or correct for bias or optimise the performance and CO2 usage.\n\n### Thread Summaries (opt-in)\n\n**Rating:** 🟢/🟡/🟠/🔴\n\nThe rating depends on the installed text processing backend. See [the rating overview](https://docs.nextcloud.com/server/latest/admin_manual/ai/index.html) for details.\n\nLearn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/)." : "**💌 Un'applicazione di posta elettronica per Nextcloud**\n\n- **🚀 Integrazione con altre applicazioni Nextcloud!** Attualmente Contatti, Calendario e File - ne arriveranno altre.\n- **📥 Account di posta multipli!** Account personali e aziendali? Nessun problema, e una bella casella di posta unificata. Collega qualsiasi account IMAP.\n- **🔒 Invia e ricevi email cifrate!** Utilizzando l'estensione del browser [Mailvelope](https://mailvelope.com).\n- **🙈 Non stiamo reinventando la ruota!** Basata sulle ottime librerie di [Horde](https://horde.org).\n- **📬 Vuoi ospitare il tuo server di posta?** Non dobbiamo reimplementarlo poiché puoi configurare [Mail-in-a-Box](https://mailinabox.email)!\n\n## Valutazione etica dell'IA\n\n### Posta in arrivo prioritaria\n\nPositivo:\n* Il software per l'addestramento e l'inferenza di questo modello è open source.\n* Il modello è creato e addestrato in locale basandosi sui dati dell'utente.\n* I dati di addestramento sono accessibili all'utente, rendendo possibile controllare o correggere le soglie o ottimizzare le prestazioni e l'utilizzo di CO2.\n\n### Riassunti delle discussioni (opt-in)\n\n**Valutazione:** 🟢/🟡/🟠/🔴\n\nLa valutazione dipende dal backend di elaborazione del testo installato. Vedi [la panoramica della valutazione](https://docs.nextcloud.com/server/latest/admin_manual/ai/index.html) per i dettagli.\n\nScopri di più sulla Valutazione etica dell'IA di Nextcloud [nel nostro blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/).", "Your session has expired. The page will be reloaded." : "La sessione è scaduta. La pagina sarà ricaricata.", @@ -29,6 +34,7 @@ "Sent messages are saved in:" : "I messaggi inviati sono salvati in:", "Deleted messages are moved in:" : "I messaggi eliminati sono spostati in:", "Archived messages are moved in:" : "I messaggi archiviati sono spostati in:", + "Snoozed messages are moved in:" : "I messaggi rimandati vengono spostati in:", "Junk messages are saved in:" : "I messaggi indesiderati sono salvati in:", "Connecting" : "Connessione in corso", "Reconnect Google account" : "Ricollega l'account Google", @@ -37,17 +43,31 @@ "Sign in with Microsoft" : "Accedi con Microsoft", "Save" : "Salva", "Connect" : "Connetti", + "Looking up configuration" : "Recupero della configurazione", "Checking mail host connectivity" : "Verifica della connettività dell'host di posta", + "Configuration discovery failed. Please use the manual settings" : "Rilevamento automatico della configurazione non riuscito. Si prega di utilizzare le impostazioni manuali.", "Password required" : "Password richiesta", + "Testing authentication" : "Verifica dell’autenticazione in corso", "Awaiting user consent" : "In attesa del consenso dell'utente.", + "Account created. Please follow the pop-up instructions to link your Google account" : "Account creato. Seguire le istruzioni del pop-up per collegare il proprio account Google", + "Account created. Please follow the pop-up instructions to link your Microsoft account" : "Account creato. Seguire le istruzioni del pop-up per collegare il proprio account Microsoft", "Loading account" : "Caricamento account", + "Account updated. Please follow the pop-up instructions to reconnect your Google account" : "Account aggiornato. Seguire le istruzioni del pop-up per riconnettere il proprio account Google", + "Account updated. Please follow the pop-up instructions to reconnect your Microsoft account" : "Account aggiornato. Seguire le istruzioni del pop-up per riconnettere il proprio account Microsoft", "Account updated" : "Account aggiornato", "IMAP server is not reachable" : "Il server IMAP non è raggiungibile", "SMTP server is not reachable" : "Il server SMTP non è raggiungibile", "IMAP username or password is wrong" : "Il nome utente o la password IMAP sono errati.", "SMTP username or password is wrong" : "Il nome utente o la password SMTP sono errati.", + "IMAP server denied authentication" : "Autenticazione negata dal server IMAP", + "SMTP server denied authentication" : "Autenticazione negata dal server SMTP", + "IMAP authentication error" : "Errore di autenticazione IMAP", + "SMTP authentication error" : "Errore di autenticazione SMTP", "IMAP connection failed" : "Connessione IMAP non riuscita", "SMTP connection failed" : "Connessione SMTP non riuscita", + "Authorization pop-up closed" : "La finestra pop-up di autorizzazione è stata chiusa", + "Configuration discovery temporarily not available. Please try again later." : "Il rilevamento della configurazione è temporaneamente non disponibile. Riprovare più tardi.", + "There was an error while setting up your account" : "Si è verificato un errore durante la configurazione dell’account.", "Auto" : "Automatico", "Name" : "Nome", "Mail address" : "Indirizzo di posta", @@ -71,6 +91,8 @@ "SMTP Port" : "Porta SMTP", "SMTP User" : "Utente SMTP", "SMTP Password" : "Password SMTP", + "Google requires OAuth authentication. If your Nextcloud admin has not configured Google OAuth, you can use a Google App Password instead." : "Google richiede l’autenticazione OAuth. Se l’amministratore di Nextcloud non ha configurato Google OAuth, è possibile utilizzare una password per app Google.", + "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "Microsoft richiede l’autenticazione OAuth. Si prega di contattare l’amministratore di Nextcloud per configurare OAuth di Microsoft nelle impostazioni di amministrazione.", "Account settings" : "Impostazioni account", "Aliases" : "Alias", "Alias to S/MIME certificate mapping" : "Mappatura alias e certificati S/MIME", @@ -106,11 +128,13 @@ "Cancel" : "Annulla", "Search the body of messages in priority Inbox" : "Cerca nel corpo dei messaggi nella posta prioritaria", "Activate" : "Attiva", + "Remind about messages that require a reply but received none" : "Promemoria per i messaggi che richiedono una risposta ma non hanno ricevuto alcun riscontro", "Highlight external addresses" : "Evidenzia indirizzi esterni", "Could not update preference" : "Impossibile aggiornare la preferenza", "Mail settings" : "Impostazioni Posta", "General" : "Generale", "Set as default mail app" : "Imposta come applicazione di posta predefinita", + "{email} (delegated)" : "{email} (delegata)", "Add mail account" : "Aggiungi account di posta", "Appearance" : "Aspetto", "Show all messages in thread" : "Mostra tutti i messaggi della conversazione", @@ -142,8 +166,12 @@ "S/MIME" : "S/MIME", "Manage certificates" : "Gestisci certificati", "Mailvelope" : "Mailvelope", + "Mailvelope is enabled for the current domain." : "Mailvelope è abilitato per il dominio corrente.", + "Step 1" : "Passo 1", "Install the browser extension" : "Installa l'estensione del browser", + "Step 2" : "Passo 2", "Enable for the current domain" : "Abilita per il dominio corrente", + "Assistance features" : "Funzionalità di assistenza", "Compose new message" : "Componi nuovo messaggio", "Newer message" : "Messaggio più recente", "Older message" : "Messaggio più datato", @@ -156,13 +184,19 @@ "Refresh" : "Aggiorna", "About" : "Informazioni", "Acknowledgements" : "Ringraziamenti", + "This application includes CKEditor, an open-source editor. Copyright © CKEditor contributors. Licensed under GPLv2." : "Questa applicazione include CKEditor, un editor open-source. Copyright © dei contributori di CKEditor. Concesso in licenza GPLv2.", + "Title of the text block" : "Titolo del blocco di testo", + "Content of the text block" : "Contenuto del blocco di testo", "Ok" : "Ok", + "Automatically create tentative appointments in calendar" : "Crea automaticamente appuntamenti provvisori nel calendario", "No certificate" : "Nessun certificato", "Certificate updated" : "Certificato aggiornato", "Could not update certificate" : "Impossibile aggiornare il certificato", "{commonName} - Valid until {expiryDate}" : "{commonName} - Valido fino al {expiryDate}", "Select an alias" : "Seleziona un alias", + "Select certificates" : "Seleziona i certificati", "Update Certificate" : "Aggiorna certificato", + "The selected certificate is not trusted by the server. Recipients might not be able to verify your signature." : "Il certificato selezionato non è considerato attendibile dal server. I destinatari potrebbero non essere in grado di verificare la tua firma.", "Encrypt with S/MIME and send later" : "Cifra con S/MIME e invia in seguito", "Encrypt with S/MIME and send" : "Cifra con S/MIME e invia", "Encrypt with Mailvelope and send later" : "Cifra con Mailvelope e invia in seguito", diff --git a/l10n/pt_BR.js b/l10n/pt_BR.js index 7b93cd8330..be010e9abc 100644 --- a/l10n/pt_BR.js +++ b/l10n/pt_BR.js @@ -94,7 +94,7 @@ OC.L10N.register( "SMTP User" : "Usuário SMTP", "SMTP Password" : "Senha SMTP", "Google requires OAuth authentication. If your Nextcloud admin has not configured Google OAuth, you can use a Google App Password instead." : "O Google exige autenticação OAuth. Se a administração do Nextcloud não tiver configurado o OAuth do Google, você pode usar uma senha de aplicativo do Google.", - "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "O Microsoft exige autenticação OAuth. Peça à administração do Nextcloud para configurar o OAuth da Microsoft nas configurações de administração.", + "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "O Microsoft exige autenticação OAuth. Peça à administração do Nextcloud para configurar o OAuth do Microsoft nas configurações de administração.", "Account settings" : "Configurações de conta", "Aliases" : "Aliases", "Alias to S/MIME certificate mapping" : "Mapeamento de alias para certificado S/MIME", @@ -137,6 +137,7 @@ OC.L10N.register( "Mail settings" : "Configurações de e-mail", "General" : "Geral", "Set as default mail app" : "Definir como aplicativo de e-mail padrão", + "{email} (delegated)" : "{email} (delegado)", "Add mail account" : "Adicionar conta de e-mail", "Appearance" : "Aparência", "Show all messages in thread" : "Mostrar todas as mensagens no fio", @@ -255,7 +256,21 @@ OC.L10N.register( "Expand composer" : "Expandir compositor", "Close composer" : "Fechar compositor", "Confirm" : "Confirmar", + "Delegate access" : "Delegar acesso", "Revoke" : "Revogar", + "{userId} will no longer be able to act on your behalf" : "{userId} não poderá mais agir em seu nome", + "Could not fetch delegates" : "Não foi possível obter os delegados", + "Delegated access to {userId}" : "Delegar acesso a {userId}", + "Could not delegate access" : "Não foi possível delegar o acesso", + "Revoked access for {userId}" : "Acesso revogado de {userId}", + "Could not revoke delegation" : "Não foi possível revogar a delegação", + "Delegation" : "Delegação", + "Allow users to send, receive, and delete mail on your behalf" : "Permita que os usuários enviem, recebam e excluam e-mails em seu nome", + "Revoke access" : "Revogar acesso", + "Add delegate" : "Adicionar delegado", + "Select a user" : "Selecione um usuário", + "They will be able to send, receive, and delete mail on your behalf" : "Eles poderão enviar, receber e excluir e-mails em seu nome", + "Revoke access?" : "Revogar acesso?", "Tag: {name} deleted" : "Etiqueta: {name} excluída", "An error occurred, unable to delete the tag." : "Ocorreu um erro, não foi possível excluir a etiqueta.", "The tag will be deleted from all messages." : "A etiqueta será excluída de todas as mensagens.", @@ -452,8 +467,10 @@ OC.L10N.register( "Remove account" : "Excluir a conta", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "A conta do e-mail {email} e os dados em cache serão removidos do Nextcloud, mas não do seu provedor de e-mail.", "Remove {email}" : "Excluir {email}", + "could not delete account" : "não foi possível excluir a conta", "Provisioned account is disabled" : "A conta provisionada está desativada", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "Faça o login usando uma senha para habilitar esta conta. A sessão atual está usando autenticação sem senha, p. ex., SSO ou WebAuthn.", + "Delegate account" : "Delegar conta", "Show only subscribed folders" : "Mostrar apenas pastas subscritas", "Add folder" : "Adicionar pasta", "Folder name" : "Nome da pasta", diff --git a/l10n/pt_BR.json b/l10n/pt_BR.json index 11c8a206ba..4c61eec5d5 100644 --- a/l10n/pt_BR.json +++ b/l10n/pt_BR.json @@ -92,7 +92,7 @@ "SMTP User" : "Usuário SMTP", "SMTP Password" : "Senha SMTP", "Google requires OAuth authentication. If your Nextcloud admin has not configured Google OAuth, you can use a Google App Password instead." : "O Google exige autenticação OAuth. Se a administração do Nextcloud não tiver configurado o OAuth do Google, você pode usar uma senha de aplicativo do Google.", - "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "O Microsoft exige autenticação OAuth. Peça à administração do Nextcloud para configurar o OAuth da Microsoft nas configurações de administração.", + "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "O Microsoft exige autenticação OAuth. Peça à administração do Nextcloud para configurar o OAuth do Microsoft nas configurações de administração.", "Account settings" : "Configurações de conta", "Aliases" : "Aliases", "Alias to S/MIME certificate mapping" : "Mapeamento de alias para certificado S/MIME", @@ -135,6 +135,7 @@ "Mail settings" : "Configurações de e-mail", "General" : "Geral", "Set as default mail app" : "Definir como aplicativo de e-mail padrão", + "{email} (delegated)" : "{email} (delegado)", "Add mail account" : "Adicionar conta de e-mail", "Appearance" : "Aparência", "Show all messages in thread" : "Mostrar todas as mensagens no fio", @@ -253,7 +254,21 @@ "Expand composer" : "Expandir compositor", "Close composer" : "Fechar compositor", "Confirm" : "Confirmar", + "Delegate access" : "Delegar acesso", "Revoke" : "Revogar", + "{userId} will no longer be able to act on your behalf" : "{userId} não poderá mais agir em seu nome", + "Could not fetch delegates" : "Não foi possível obter os delegados", + "Delegated access to {userId}" : "Delegar acesso a {userId}", + "Could not delegate access" : "Não foi possível delegar o acesso", + "Revoked access for {userId}" : "Acesso revogado de {userId}", + "Could not revoke delegation" : "Não foi possível revogar a delegação", + "Delegation" : "Delegação", + "Allow users to send, receive, and delete mail on your behalf" : "Permita que os usuários enviem, recebam e excluam e-mails em seu nome", + "Revoke access" : "Revogar acesso", + "Add delegate" : "Adicionar delegado", + "Select a user" : "Selecione um usuário", + "They will be able to send, receive, and delete mail on your behalf" : "Eles poderão enviar, receber e excluir e-mails em seu nome", + "Revoke access?" : "Revogar acesso?", "Tag: {name} deleted" : "Etiqueta: {name} excluída", "An error occurred, unable to delete the tag." : "Ocorreu um erro, não foi possível excluir a etiqueta.", "The tag will be deleted from all messages." : "A etiqueta será excluída de todas as mensagens.", @@ -450,8 +465,10 @@ "Remove account" : "Excluir a conta", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "A conta do e-mail {email} e os dados em cache serão removidos do Nextcloud, mas não do seu provedor de e-mail.", "Remove {email}" : "Excluir {email}", + "could not delete account" : "não foi possível excluir a conta", "Provisioned account is disabled" : "A conta provisionada está desativada", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "Faça o login usando uma senha para habilitar esta conta. A sessão atual está usando autenticação sem senha, p. ex., SSO ou WebAuthn.", + "Delegate account" : "Delegar conta", "Show only subscribed folders" : "Mostrar apenas pastas subscritas", "Add folder" : "Adicionar pasta", "Folder name" : "Nome da pasta", diff --git a/l10n/zh_CN.js b/l10n/zh_CN.js index 400f7d41d5..62af5d0d07 100644 --- a/l10n/zh_CN.js +++ b/l10n/zh_CN.js @@ -93,6 +93,8 @@ OC.L10N.register( "SMTP Port" : "SMTP 端口", "SMTP User" : "SMTP 用户", "SMTP Password" : "SMTP 密码", + "Google requires OAuth authentication. If your Nextcloud admin has not configured Google OAuth, you can use a Google App Password instead." : "Google 需要 OAuth 身份验证。如果您的 Nextcloud 管理员尚未配置 Google OAuth,您可以使用 Google 应用密码代替。", + "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "Microsoft 需要使用 OAuth 身份验证。请您的 Nextcloud 管理员在管理员设置中配置 Microsoft OAuth。", "Account settings" : "账号设置", "Aliases" : "别名", "Alias to S/MIME certificate mapping" : "別名到 S/MIME 证书映射", @@ -135,6 +137,7 @@ OC.L10N.register( "Mail settings" : "邮件设置", "General" : "常规", "Set as default mail app" : "设为默认邮件应用", + "{email} (delegated)" : "{email}(已委派)", "Add mail account" : "添加邮件账号", "Appearance" : "外观", "Show all messages in thread" : "显示线程中的所有邮件", @@ -253,7 +256,21 @@ OC.L10N.register( "Expand composer" : "展开编辑器", "Close composer" : "关闭编辑器", "Confirm" : "确认", + "Delegate access" : "委派访问权限", "Revoke" : "撤销", + "{userId} will no longer be able to act on your behalf" : "{userId} 将无法再代表您执行操作", + "Could not fetch delegates" : "无法获取委派用户", + "Delegated access to {userId}" : "已向 {userId} 委派访问权限", + "Could not delegate access" : "无法委派访问权限", + "Revoked access for {userId}" : "已撤销 {userId} 的访问权限", + "Could not revoke delegation" : "无法撤销委派", + "Delegation" : "委派", + "Allow users to send, receive, and delete mail on your behalf" : "允许用户代表您发送、接收和删除邮件", + "Revoke access" : "撤销访问权限", + "Add delegate" : "添加委派", + "Select a user" : "选择用户", + "They will be able to send, receive, and delete mail on your behalf" : "他们将能够代表您发送、接收和删除邮件", + "Revoke access?" : "撤销访问权限?", "Tag: {name} deleted" : "标签:{name} 已删除", "An error occurred, unable to delete the tag." : "发生错误,无法删除标签。", "The tag will be deleted from all messages." : "将从所有邮件中删除此标签。", @@ -450,8 +467,10 @@ OC.L10N.register( "Remove account" : "移除账号", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "{email}和缓存的电子邮箱数据将从 Nextcloud 账号中删除,但不会从你的电子邮箱提供商那里删除。", "Remove {email}" : "移除 {email}", + "could not delete account" : "无法删除账号", "Provisioned account is disabled" : "预配账号已禁用", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "请使用密码登录以启用此账号。当前会话正在使用无密码身份验证,例如 SSO 或 WebAuthn。", + "Delegate account" : "委派账号", "Show only subscribed folders" : "仅显示已订阅的文件夹", "Add folder" : "添加文件夹", "Folder name" : "文件夹名称", @@ -625,6 +644,8 @@ OC.L10N.register( "Mark as unfavorite" : "取消标记为收藏", "Mark as favorite" : "标记为收藏", "To:" : "到:", + "Cc:" : "抄送:", + "Bcc:" : "密送:", "Unsubscribe via link" : "通过链接取消订阅", "Unsubscribing will stop all messages from the mailing list {sender}" : "取消订阅将停止来自邮寄列表 {sender} 的全部邮件", "Send unsubscribe email" : "发送取消订阅电子邮件", diff --git a/l10n/zh_CN.json b/l10n/zh_CN.json index 646b414a99..4f5aaad39d 100644 --- a/l10n/zh_CN.json +++ b/l10n/zh_CN.json @@ -91,6 +91,8 @@ "SMTP Port" : "SMTP 端口", "SMTP User" : "SMTP 用户", "SMTP Password" : "SMTP 密码", + "Google requires OAuth authentication. If your Nextcloud admin has not configured Google OAuth, you can use a Google App Password instead." : "Google 需要 OAuth 身份验证。如果您的 Nextcloud 管理员尚未配置 Google OAuth,您可以使用 Google 应用密码代替。", + "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "Microsoft 需要使用 OAuth 身份验证。请您的 Nextcloud 管理员在管理员设置中配置 Microsoft OAuth。", "Account settings" : "账号设置", "Aliases" : "别名", "Alias to S/MIME certificate mapping" : "別名到 S/MIME 证书映射", @@ -133,6 +135,7 @@ "Mail settings" : "邮件设置", "General" : "常规", "Set as default mail app" : "设为默认邮件应用", + "{email} (delegated)" : "{email}(已委派)", "Add mail account" : "添加邮件账号", "Appearance" : "外观", "Show all messages in thread" : "显示线程中的所有邮件", @@ -251,7 +254,21 @@ "Expand composer" : "展开编辑器", "Close composer" : "关闭编辑器", "Confirm" : "确认", + "Delegate access" : "委派访问权限", "Revoke" : "撤销", + "{userId} will no longer be able to act on your behalf" : "{userId} 将无法再代表您执行操作", + "Could not fetch delegates" : "无法获取委派用户", + "Delegated access to {userId}" : "已向 {userId} 委派访问权限", + "Could not delegate access" : "无法委派访问权限", + "Revoked access for {userId}" : "已撤销 {userId} 的访问权限", + "Could not revoke delegation" : "无法撤销委派", + "Delegation" : "委派", + "Allow users to send, receive, and delete mail on your behalf" : "允许用户代表您发送、接收和删除邮件", + "Revoke access" : "撤销访问权限", + "Add delegate" : "添加委派", + "Select a user" : "选择用户", + "They will be able to send, receive, and delete mail on your behalf" : "他们将能够代表您发送、接收和删除邮件", + "Revoke access?" : "撤销访问权限?", "Tag: {name} deleted" : "标签:{name} 已删除", "An error occurred, unable to delete the tag." : "发生错误,无法删除标签。", "The tag will be deleted from all messages." : "将从所有邮件中删除此标签。", @@ -448,8 +465,10 @@ "Remove account" : "移除账号", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "{email}和缓存的电子邮箱数据将从 Nextcloud 账号中删除,但不会从你的电子邮箱提供商那里删除。", "Remove {email}" : "移除 {email}", + "could not delete account" : "无法删除账号", "Provisioned account is disabled" : "预配账号已禁用", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "请使用密码登录以启用此账号。当前会话正在使用无密码身份验证,例如 SSO 或 WebAuthn。", + "Delegate account" : "委派账号", "Show only subscribed folders" : "仅显示已订阅的文件夹", "Add folder" : "添加文件夹", "Folder name" : "文件夹名称", @@ -623,6 +642,8 @@ "Mark as unfavorite" : "取消标记为收藏", "Mark as favorite" : "标记为收藏", "To:" : "到:", + "Cc:" : "抄送:", + "Bcc:" : "密送:", "Unsubscribe via link" : "通过链接取消订阅", "Unsubscribing will stop all messages from the mailing list {sender}" : "取消订阅将停止来自邮寄列表 {sender} 的全部邮件", "Send unsubscribe email" : "发送取消订阅电子邮件", From 78383856e07df0c2a7ba0bd2f7f8e66b7ddcf69c Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Sun, 17 May 2026 01:33:35 +0000 Subject: [PATCH 034/228] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- l10n/lt_LT.js | 49 +++++++++++++++++++++++++++++++++++-------------- l10n/lt_LT.json | 49 +++++++++++++++++++++++++++++++++++-------------- l10n/pt_PT.js | 1 + l10n/pt_PT.json | 1 + 4 files changed, 72 insertions(+), 28 deletions(-) diff --git a/l10n/lt_LT.js b/l10n/lt_LT.js index 16860f5b3c..d16af2a50d 100644 --- a/l10n/lt_LT.js +++ b/l10n/lt_LT.js @@ -93,6 +93,8 @@ OC.L10N.register( "SMTP Port" : "SMTP prievadas", "SMTP User" : "SMTP naudotojo vardas", "SMTP Password" : "SMTP slaptažodis", + "Google requires OAuth authentication. If your Nextcloud admin has not configured Google OAuth, you can use a Google App Password instead." : "„Google“ reikalauja „OAuth“ autentifikavimo. Jei jūsų „Nextcloud“ administratorius nesukonfigūravo „Google OAuth“, galite naudoti „Google“ programos slaptažodį.", + "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "„Microsoft“ reikalauja „OAuth“ autentifikavimo. Paprašykite „Nextcloud“ administratoriaus sukonfigūruoti „Microsoft OAuth“ administratoriaus nustatymuose.", "Account settings" : "Paskyros nustatymai", "Aliases" : "Pseudonimai", "Alias to S/MIME certificate mapping" : "Slapyvardžio ir S/MIME sertifikato susiejimas", @@ -135,6 +137,7 @@ OC.L10N.register( "Mail settings" : "Pašto nustatymai", "General" : "Bendra", "Set as default mail app" : "Nustatyti kaip numatytąją pašto programėlę", + "{email} (delegated)" : "{email} (deleguotas)", "Add mail account" : "Pridėti pašto paskyrą", "Appearance" : "Išvaizda", "Show all messages in thread" : "Rodyti visus šio pokalbio pranešimus", @@ -146,7 +149,7 @@ OC.L10N.register( "Horizontal split" : "Padalijimas horizontaliai", "List" : "Sąrašas", "Use compact mode" : "Naudoti kompaktiškąjį režimą", - "Sorting" : "Rikiavimas", + "Sorting" : "Rūšiavimas", "Newest first" : "Naujausi pirma", "Oldest first" : "Seniausi pirma", "Avatars from Gravatar and favicons" : "Avatarai iš „Gravatar“ ir svetainių piktogramos", @@ -164,7 +167,7 @@ OC.L10N.register( "Security" : "Saugumas", "Manage your internal addresses and domains to ensure recognized contacts stay unmarked" : "Tvarkykite savo vidinius adresus ir domenus, kad atpažinti kontaktai neliktų pažymėti", "S/MIME" : "S/MIME", - "Manage certificates" : "Tvarkyti liudijimus", + "Manage certificates" : "Tvarkyti sertifikatus", "Mailvelope" : "„Mailvelope“", "Mailvelope is enabled for the current domain." : "„Mailvelope“ yra įjungta dabartiniam domenui.", "Step 1" : "1 žingsnis", @@ -233,7 +236,7 @@ OC.L10N.register( "Upload attachment" : "Įkelti priedą", "Add attachment from Files" : "Pridėti priedą iš Failų", "Add share link from Files" : "Pridėti bendrinimo nuorodą iš Failų", - "Smart picker" : "\"Smart picker\"", + "Smart picker" : "„Išmanusis rinkiklis“", "Request a read receipt" : "Paprašyti patvirtinimo apie perskaitymą", "Sign message with S/MIME" : "Pasirašyti laišką naudojant S/MIME", "Encrypt message with S/MIME" : "Šifruoti laišką naudojant S/MIME", @@ -253,7 +256,21 @@ OC.L10N.register( "Expand composer" : "Išskleisti rašymo langą", "Close composer" : "Uždaryti rašymo langą", "Confirm" : "Patvirtinti", + "Delegate access" : "Deleguoti prieigą", "Revoke" : "Panaikinti", + "{userId} will no longer be able to act on your behalf" : "{userId} nebegalės veikti jūsų vardu", + "Could not fetch delegates" : "Nepavyko gauti delegatų", + "Delegated access to {userId}" : "Deleguota prieiga prie {userId}", + "Could not delegate access" : "Nepavyko deleguoti prieigos", + "Revoked access for {userId}" : "Atšaukta prieiga {userId}", + "Could not revoke delegation" : "Nepavyko atšaukti delegavimo", + "Delegation" : "Delegavimas", + "Allow users to send, receive, and delete mail on your behalf" : "Leisti vartotojams siųsti, gauti ir ištrinti el. laiškus jūsų vardu", + "Revoke access" : "Atšaukti prieigą", + "Add delegate" : "Pridėti delegatą", + "Select a user" : "Pasirinkite vartotoją", + "They will be able to send, receive, and delete mail on your behalf" : "Jie galės siųsti, gauti ir ištrinti el. laiškus jūsų vardu", + "Revoke access?" : "Atšaukti prieigą?", "Tag: {name} deleted" : "Žyma: {name} ištrinta", "An error occurred, unable to delete the tag." : "Įvyko klaida, nepavyko ištrinti žymos.", "The tag will be deleted from all messages." : "Žymė bus ištrinta iš visų pranešimų.", @@ -305,7 +322,7 @@ OC.L10N.register( "More actions" : "Daugiau veiksmų", "Back" : "Atgal", "Set custom snooze" : "Nustatyti pasirinktinį atidėjimą", - "Edit as new message" : "Taisyti kaip naują laišką", + "Edit as new message" : "Redaguoti kaip naują laišką", "Reply with meeting" : "Atsisakyti siūlant susitikimą", "Create task" : "Sukurti užduotį", "Download message" : "Atsisiųsti laišką", @@ -450,8 +467,10 @@ OC.L10N.register( "Remove account" : "Šalinti paskyrą", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "Paskyra {email} ir podėlyje esantys el. pašto duomenys bus pašalinti iš Nextcloud, tačiau nebus pašalinti iš jūsų el. pašto teikėjo puslapio.", "Remove {email}" : "Šalinti {email}", + "could not delete account" : "nepavyko ištrinti paskyros", "Provisioned account is disabled" : "Parengtoji paskyra yra išjungta", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "Norėdami aktyvuoti šią paskyrą, prisijunkite naudodami slaptažodį. Dabartinė sesija naudoja autentifikavimą be slaptažodžio, pvz., SSO arba WebAuthn.", + "Delegate account" : "Deleguoti paskyrą", "Show only subscribed folders" : "Rodyti tik prenumeruotus aplankus", "Add folder" : "Pridėti aplanką", "Folder name" : "Aplanko pavadinimas", @@ -485,7 +504,7 @@ OC.L10N.register( "Outbox" : "Išsiuntimo dėžutė", "Translate this message to {language}" : "Versti šį laišką į {language} kalbą", "New message" : "Naujas laiškas", - "Edit message" : "Taisyti laišką", + "Edit message" : "Redaguoti laišką", "Draft" : "Juodraštis", "Reply" : "Atsakyti", "Message saved" : "Laiškas įrašytas", @@ -537,7 +556,7 @@ OC.L10N.register( "Could not copy email address to clipboard" : "Nepavyko nukopijuoti el. pašto adreso į iškarpinę", "Contacts with this address" : "Adresatai turintys šį adresą", "Add to Contact" : "Pridėti prie „Kontaktai“", - "New Contact" : "Naujas adresatas", + "New Contact" : "Naujas kontaktas", "Copy to clipboard" : "Kopijuoti į iškarpinę", "Contact name …" : "Adresato vardas …", "Add" : "Pridėti", @@ -625,6 +644,8 @@ OC.L10N.register( "Mark as unfavorite" : "Pažymėti kaip nepatinkantį", "Mark as favorite" : "Žymėti kaip mėgstamiausią", "To:" : "Iki:", + "Cc:" : "Cc:", + "Bcc:" : "Bcc:", "Unsubscribe via link" : "Atsisakyti prenumeratos per nuorodą", "Unsubscribing will stop all messages from the mailing list {sender}" : "Atsisakę prenumeratos, nebegausite jokių laiškų iš šio pašto sąrašo {sender}", "Send unsubscribe email" : "Siųsti laišką dėl prenumeratos atsisakymo", @@ -717,7 +738,7 @@ OC.L10N.register( "Quick action created" : "Greitasis veiksmas sukurtas", "Failed to delete action step" : "Nepavyko ištrinti veiksmo žingsnio", "No quick actions yet." : "Kol kas jokių greitųjų veiksmų.", - "Edit" : "Taisyti", + "Edit" : "Redaguoti", "Quick action name" : "Greitojo veiksmo pavadinimas", "Do the following actions" : "Atlikti šiuos veiksmus", "Add another action" : "Pridėti kitą veiksmą", @@ -731,8 +752,8 @@ OC.L10N.register( "Error when deleting and deprovisioning accounts for \"{domain}\"" : "Klaida trinant ir atšaukiant „{domain}“ srities paskyrų parengimą", "Could not save default classification setting" : "Nepavyko išsaugoti numatytojo klasifikavimo nustatymo", "Mail app" : "Pašto programėlė", - "The mail app allows users to read mails on their IMAP accounts." : "Pašto programėlė leidžia naudotojams skaityti laiškus savo IMAP paskyrose.", - "Here you can find instance-wide settings. User specific settings are found in the app itself (bottom-left corner)." : "Čia galite rasti egzemplioriaus masto nustatymus. Specifinius naudotojo nustatymus galite rasti pačioje programėlėje (apatiniame kairiajame kampe).", + "The mail app allows users to read mails on their IMAP accounts." : "Pašto programėlė leidžia vartotojams skaityti laiškus savo IMAP paskyrose.", + "Here you can find instance-wide settings. User specific settings are found in the app itself (bottom-left corner)." : "Čia galite rasti egzemplioriaus masto nustatymus. Specifinius vartotojo nustatymus galite rasti pačioje programėlėje (apatiniame kairiajame kampe).", "Account provisioning" : "Paskyros parengimas", "A provisioning configuration will provision all accounts with a matching email address." : "Naudojant parengimo konfigūraciją, bus sukurtos visos atitinkamą el. pašto adresą turinčios paskyros.", "Using the wildcard (*) in the provisioning domain field will create a configuration that applies to all users, provided they do not match another configuration." : "Naudojant pakaitos simbolį (*) parengimo domeno lauke, bus sukurta konfigūracija, kuri bus taikoma visiems vartotojams, jei jie neatitinka kitos konfigūracijos.", @@ -799,7 +820,7 @@ OC.L10N.register( "Provisioning domain" : "Parengimo domenas", "Email address template" : "El. pašto adreso šablonas", "IMAP" : "IMAP", - "User" : "Naudotojas", + "User" : "Vartotojas", "Host" : "Serveris", "Port" : "Prievadas", "SMTP" : "SMTP", @@ -846,17 +867,17 @@ OC.L10N.register( "Group" : "Grupė", "Failed to save text block" : "Nepavyko išsaugoti teksto bloko", "Shared" : "Bendrinama", - "Edit {title}" : "Taisyti {title}", + "Edit {title}" : "Redaguoti {title}", "Delete {title}" : "Ištrinti {title}", "Edit text block" : "Redaguoti teksto bloką", - "Shares" : "Viešiniai", + "Shares" : "Bendrinimai", "Search for users or groups" : "Ieškoti vartotojų arba grupių", "Choose a text block to insert at the cursor" : "Pasirinkite teksto bloką, kurį norite įterpti ties žymekliu", "Insert" : "Įterpti", "Insert text block" : "Įterpti teksto bloką", "Account connected" : "Paskyra prijungta", "You can close this window" : "Galite užverti šį langą", - "Connect your mail account" : "Priregistruoti elektroninio pašto paskyrą", + "Connect your mail account" : "Pridėti elektroninio pašto paskyrą", "To add a mail account, please contact your administrator." : "Norėdami pridėti pašto paskyrą, susisiekite su savo administratoriumi.", "All" : "Visos", "Drafts" : "Juodraščiai", @@ -874,7 +895,7 @@ OC.L10N.register( "There is already a message in progress. All unsaved changes will be lost if you continue!" : "Jau vyksta žinutės rašymas. Jei tęsite, visi neišsaugoti pakeitimai bus prarasti!", "Discard changes" : "Atmesti pakeitimus", "Discard unsaved changes" : "Atmesti neįrašytus pakeitimus", - "Keep editing message" : "Toliau taisyti laišką", + "Keep editing message" : "Toliau redaguoti laišką", "(All or part of this reply was generated by AI)" : "(Visas šis atsakymas arba jo dalis buvo sugeneruota dirbtinio intelekto)", "Attachments were not copied. Please add them manually." : "Priedai nebuvo nukopijuoti. Pridėkite juos rankiniu būdu.", "Could not create snooze mailbox" : "Nepavyko sukurti atidėjimo pašto dėžutės", diff --git a/l10n/lt_LT.json b/l10n/lt_LT.json index 9860c7a79b..b16a0ae969 100644 --- a/l10n/lt_LT.json +++ b/l10n/lt_LT.json @@ -91,6 +91,8 @@ "SMTP Port" : "SMTP prievadas", "SMTP User" : "SMTP naudotojo vardas", "SMTP Password" : "SMTP slaptažodis", + "Google requires OAuth authentication. If your Nextcloud admin has not configured Google OAuth, you can use a Google App Password instead." : "„Google“ reikalauja „OAuth“ autentifikavimo. Jei jūsų „Nextcloud“ administratorius nesukonfigūravo „Google OAuth“, galite naudoti „Google“ programos slaptažodį.", + "Microsoft requires OAuth authentication. Ask your Nextcloud admin to configure Microsoft OAuth in the admin settings." : "„Microsoft“ reikalauja „OAuth“ autentifikavimo. Paprašykite „Nextcloud“ administratoriaus sukonfigūruoti „Microsoft OAuth“ administratoriaus nustatymuose.", "Account settings" : "Paskyros nustatymai", "Aliases" : "Pseudonimai", "Alias to S/MIME certificate mapping" : "Slapyvardžio ir S/MIME sertifikato susiejimas", @@ -133,6 +135,7 @@ "Mail settings" : "Pašto nustatymai", "General" : "Bendra", "Set as default mail app" : "Nustatyti kaip numatytąją pašto programėlę", + "{email} (delegated)" : "{email} (deleguotas)", "Add mail account" : "Pridėti pašto paskyrą", "Appearance" : "Išvaizda", "Show all messages in thread" : "Rodyti visus šio pokalbio pranešimus", @@ -144,7 +147,7 @@ "Horizontal split" : "Padalijimas horizontaliai", "List" : "Sąrašas", "Use compact mode" : "Naudoti kompaktiškąjį režimą", - "Sorting" : "Rikiavimas", + "Sorting" : "Rūšiavimas", "Newest first" : "Naujausi pirma", "Oldest first" : "Seniausi pirma", "Avatars from Gravatar and favicons" : "Avatarai iš „Gravatar“ ir svetainių piktogramos", @@ -162,7 +165,7 @@ "Security" : "Saugumas", "Manage your internal addresses and domains to ensure recognized contacts stay unmarked" : "Tvarkykite savo vidinius adresus ir domenus, kad atpažinti kontaktai neliktų pažymėti", "S/MIME" : "S/MIME", - "Manage certificates" : "Tvarkyti liudijimus", + "Manage certificates" : "Tvarkyti sertifikatus", "Mailvelope" : "„Mailvelope“", "Mailvelope is enabled for the current domain." : "„Mailvelope“ yra įjungta dabartiniam domenui.", "Step 1" : "1 žingsnis", @@ -231,7 +234,7 @@ "Upload attachment" : "Įkelti priedą", "Add attachment from Files" : "Pridėti priedą iš Failų", "Add share link from Files" : "Pridėti bendrinimo nuorodą iš Failų", - "Smart picker" : "\"Smart picker\"", + "Smart picker" : "„Išmanusis rinkiklis“", "Request a read receipt" : "Paprašyti patvirtinimo apie perskaitymą", "Sign message with S/MIME" : "Pasirašyti laišką naudojant S/MIME", "Encrypt message with S/MIME" : "Šifruoti laišką naudojant S/MIME", @@ -251,7 +254,21 @@ "Expand composer" : "Išskleisti rašymo langą", "Close composer" : "Uždaryti rašymo langą", "Confirm" : "Patvirtinti", + "Delegate access" : "Deleguoti prieigą", "Revoke" : "Panaikinti", + "{userId} will no longer be able to act on your behalf" : "{userId} nebegalės veikti jūsų vardu", + "Could not fetch delegates" : "Nepavyko gauti delegatų", + "Delegated access to {userId}" : "Deleguota prieiga prie {userId}", + "Could not delegate access" : "Nepavyko deleguoti prieigos", + "Revoked access for {userId}" : "Atšaukta prieiga {userId}", + "Could not revoke delegation" : "Nepavyko atšaukti delegavimo", + "Delegation" : "Delegavimas", + "Allow users to send, receive, and delete mail on your behalf" : "Leisti vartotojams siųsti, gauti ir ištrinti el. laiškus jūsų vardu", + "Revoke access" : "Atšaukti prieigą", + "Add delegate" : "Pridėti delegatą", + "Select a user" : "Pasirinkite vartotoją", + "They will be able to send, receive, and delete mail on your behalf" : "Jie galės siųsti, gauti ir ištrinti el. laiškus jūsų vardu", + "Revoke access?" : "Atšaukti prieigą?", "Tag: {name} deleted" : "Žyma: {name} ištrinta", "An error occurred, unable to delete the tag." : "Įvyko klaida, nepavyko ištrinti žymos.", "The tag will be deleted from all messages." : "Žymė bus ištrinta iš visų pranešimų.", @@ -303,7 +320,7 @@ "More actions" : "Daugiau veiksmų", "Back" : "Atgal", "Set custom snooze" : "Nustatyti pasirinktinį atidėjimą", - "Edit as new message" : "Taisyti kaip naują laišką", + "Edit as new message" : "Redaguoti kaip naują laišką", "Reply with meeting" : "Atsisakyti siūlant susitikimą", "Create task" : "Sukurti užduotį", "Download message" : "Atsisiųsti laišką", @@ -448,8 +465,10 @@ "Remove account" : "Šalinti paskyrą", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "Paskyra {email} ir podėlyje esantys el. pašto duomenys bus pašalinti iš Nextcloud, tačiau nebus pašalinti iš jūsų el. pašto teikėjo puslapio.", "Remove {email}" : "Šalinti {email}", + "could not delete account" : "nepavyko ištrinti paskyros", "Provisioned account is disabled" : "Parengtoji paskyra yra išjungta", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "Norėdami aktyvuoti šią paskyrą, prisijunkite naudodami slaptažodį. Dabartinė sesija naudoja autentifikavimą be slaptažodžio, pvz., SSO arba WebAuthn.", + "Delegate account" : "Deleguoti paskyrą", "Show only subscribed folders" : "Rodyti tik prenumeruotus aplankus", "Add folder" : "Pridėti aplanką", "Folder name" : "Aplanko pavadinimas", @@ -483,7 +502,7 @@ "Outbox" : "Išsiuntimo dėžutė", "Translate this message to {language}" : "Versti šį laišką į {language} kalbą", "New message" : "Naujas laiškas", - "Edit message" : "Taisyti laišką", + "Edit message" : "Redaguoti laišką", "Draft" : "Juodraštis", "Reply" : "Atsakyti", "Message saved" : "Laiškas įrašytas", @@ -535,7 +554,7 @@ "Could not copy email address to clipboard" : "Nepavyko nukopijuoti el. pašto adreso į iškarpinę", "Contacts with this address" : "Adresatai turintys šį adresą", "Add to Contact" : "Pridėti prie „Kontaktai“", - "New Contact" : "Naujas adresatas", + "New Contact" : "Naujas kontaktas", "Copy to clipboard" : "Kopijuoti į iškarpinę", "Contact name …" : "Adresato vardas …", "Add" : "Pridėti", @@ -623,6 +642,8 @@ "Mark as unfavorite" : "Pažymėti kaip nepatinkantį", "Mark as favorite" : "Žymėti kaip mėgstamiausią", "To:" : "Iki:", + "Cc:" : "Cc:", + "Bcc:" : "Bcc:", "Unsubscribe via link" : "Atsisakyti prenumeratos per nuorodą", "Unsubscribing will stop all messages from the mailing list {sender}" : "Atsisakę prenumeratos, nebegausite jokių laiškų iš šio pašto sąrašo {sender}", "Send unsubscribe email" : "Siųsti laišką dėl prenumeratos atsisakymo", @@ -715,7 +736,7 @@ "Quick action created" : "Greitasis veiksmas sukurtas", "Failed to delete action step" : "Nepavyko ištrinti veiksmo žingsnio", "No quick actions yet." : "Kol kas jokių greitųjų veiksmų.", - "Edit" : "Taisyti", + "Edit" : "Redaguoti", "Quick action name" : "Greitojo veiksmo pavadinimas", "Do the following actions" : "Atlikti šiuos veiksmus", "Add another action" : "Pridėti kitą veiksmą", @@ -729,8 +750,8 @@ "Error when deleting and deprovisioning accounts for \"{domain}\"" : "Klaida trinant ir atšaukiant „{domain}“ srities paskyrų parengimą", "Could not save default classification setting" : "Nepavyko išsaugoti numatytojo klasifikavimo nustatymo", "Mail app" : "Pašto programėlė", - "The mail app allows users to read mails on their IMAP accounts." : "Pašto programėlė leidžia naudotojams skaityti laiškus savo IMAP paskyrose.", - "Here you can find instance-wide settings. User specific settings are found in the app itself (bottom-left corner)." : "Čia galite rasti egzemplioriaus masto nustatymus. Specifinius naudotojo nustatymus galite rasti pačioje programėlėje (apatiniame kairiajame kampe).", + "The mail app allows users to read mails on their IMAP accounts." : "Pašto programėlė leidžia vartotojams skaityti laiškus savo IMAP paskyrose.", + "Here you can find instance-wide settings. User specific settings are found in the app itself (bottom-left corner)." : "Čia galite rasti egzemplioriaus masto nustatymus. Specifinius vartotojo nustatymus galite rasti pačioje programėlėje (apatiniame kairiajame kampe).", "Account provisioning" : "Paskyros parengimas", "A provisioning configuration will provision all accounts with a matching email address." : "Naudojant parengimo konfigūraciją, bus sukurtos visos atitinkamą el. pašto adresą turinčios paskyros.", "Using the wildcard (*) in the provisioning domain field will create a configuration that applies to all users, provided they do not match another configuration." : "Naudojant pakaitos simbolį (*) parengimo domeno lauke, bus sukurta konfigūracija, kuri bus taikoma visiems vartotojams, jei jie neatitinka kitos konfigūracijos.", @@ -797,7 +818,7 @@ "Provisioning domain" : "Parengimo domenas", "Email address template" : "El. pašto adreso šablonas", "IMAP" : "IMAP", - "User" : "Naudotojas", + "User" : "Vartotojas", "Host" : "Serveris", "Port" : "Prievadas", "SMTP" : "SMTP", @@ -844,17 +865,17 @@ "Group" : "Grupė", "Failed to save text block" : "Nepavyko išsaugoti teksto bloko", "Shared" : "Bendrinama", - "Edit {title}" : "Taisyti {title}", + "Edit {title}" : "Redaguoti {title}", "Delete {title}" : "Ištrinti {title}", "Edit text block" : "Redaguoti teksto bloką", - "Shares" : "Viešiniai", + "Shares" : "Bendrinimai", "Search for users or groups" : "Ieškoti vartotojų arba grupių", "Choose a text block to insert at the cursor" : "Pasirinkite teksto bloką, kurį norite įterpti ties žymekliu", "Insert" : "Įterpti", "Insert text block" : "Įterpti teksto bloką", "Account connected" : "Paskyra prijungta", "You can close this window" : "Galite užverti šį langą", - "Connect your mail account" : "Priregistruoti elektroninio pašto paskyrą", + "Connect your mail account" : "Pridėti elektroninio pašto paskyrą", "To add a mail account, please contact your administrator." : "Norėdami pridėti pašto paskyrą, susisiekite su savo administratoriumi.", "All" : "Visos", "Drafts" : "Juodraščiai", @@ -872,7 +893,7 @@ "There is already a message in progress. All unsaved changes will be lost if you continue!" : "Jau vyksta žinutės rašymas. Jei tęsite, visi neišsaugoti pakeitimai bus prarasti!", "Discard changes" : "Atmesti pakeitimus", "Discard unsaved changes" : "Atmesti neįrašytus pakeitimus", - "Keep editing message" : "Toliau taisyti laišką", + "Keep editing message" : "Toliau redaguoti laišką", "(All or part of this reply was generated by AI)" : "(Visas šis atsakymas arba jo dalis buvo sugeneruota dirbtinio intelekto)", "Attachments were not copied. Please add them manually." : "Priedai nebuvo nukopijuoti. Pridėkite juos rankiniu būdu.", "Could not create snooze mailbox" : "Nepavyko sukurti atidėjimo pašto dėžutės", diff --git a/l10n/pt_PT.js b/l10n/pt_PT.js index 4e31e59534..3b64aeebec 100644 --- a/l10n/pt_PT.js +++ b/l10n/pt_PT.js @@ -192,6 +192,7 @@ OC.L10N.register( "Clear locally cached data, in case there are issues with synchronization." : "Limpe a cache de dados local no caso de ter problemas com a sincronização.", "Subscribed" : "Subscrito", "New message" : "Nova mensagem", + "Draft" : "Rascunho", "Reply" : "Resposta", "Error sending your message" : "Erro ao enviar a sua mensagem", "Retry" : "Repetir", diff --git a/l10n/pt_PT.json b/l10n/pt_PT.json index fed189a2d7..87fc06f140 100644 --- a/l10n/pt_PT.json +++ b/l10n/pt_PT.json @@ -190,6 +190,7 @@ "Clear locally cached data, in case there are issues with synchronization." : "Limpe a cache de dados local no caso de ter problemas com a sincronização.", "Subscribed" : "Subscrito", "New message" : "Nova mensagem", + "Draft" : "Rascunho", "Reply" : "Resposta", "Error sending your message" : "Erro ao enviar a sua mensagem", "Retry" : "Repetir", From 092b16dedde55060e19e1fd95cf3ff23a8c2fe72 Mon Sep 17 00:00:00 2001 From: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> Date: Mon, 18 May 2026 07:38:59 +0200 Subject: [PATCH 035/228] chore(release): v5.9.0-alpha.1 Signed-off-by: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> --- CHANGELOG.md | 8 ++++++++ appinfo/info.xml | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15a53f5ce2..9baf88b857 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Changelog All notable changes to this project will be documented in this file. +## 5.9.0 – unreleased +### Added +* Account delegation +* HTML and source editing support in composer/signature editor +* Per-message To/Cc/Bcc recipients in thread view +### Changed +* Translations + ## 5.8.0 – unreleased ### Added * Deep link support for opening messages by Message-ID diff --git a/appinfo/info.xml b/appinfo/info.xml index 9438b8376d..86f5348477 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -34,7 +34,7 @@ The rating depends on the installed text processing backend. See [the rating ove Learn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/). ]]> - 5.9.0-dev.3 + 5.9.0-alpha.1 agpl Christoph Wurst GretaD diff --git a/package-lock.json b/package-lock.json index 9f216b8a32..d5ff128a33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "nextcloud-mail", - "version": "5.9.0-dev.1", + "version": "5.9.0-alpha.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "nextcloud-mail", - "version": "5.9.0-dev.1", + "version": "5.9.0-alpha.1", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index b83de70929..dd20a52879 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nextcloud-mail", - "version": "5.9.0-dev.1", + "version": "5.9.0-alpha.1", "private": true, "description": "Nextcloud Mail", "license": "AGPL-3.0-only", From e3dcc0e0ac3b644199abb36def63c6737a89919c Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Sat, 16 May 2026 01:10:05 +0200 Subject: [PATCH 036/228] feat(deps): Add Nextcloud 35 support Signed-off-by: Joas Schilling --- appinfo/info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index 86f5348477..fd16908695 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -53,7 +53,7 @@ Learn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud https://user-images.githubusercontent.com/12728974/266270227-86b99bbb-03ea-468b-8408-e248e1730bed.png - + OCA\Mail\BackgroundJob\CleanupJob From e18b5821674588c39c42791e74390393590b8d98 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 May 2026 12:19:51 +0200 Subject: [PATCH 037/228] chore(CI): Adjust testing matrix for Nextcloud 34 on main Signed-off-by: Joas Schilling --- .github/workflows/test.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d10d7235fc..dde06cc527 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,6 +24,8 @@ jobs: nextcloud-versions: 'stable32' - php-versions: '8.2' nextcloud-versions: 'stable33' + - php-versions: '8.2' + nextcloud-versions: 'stable34' - php-versions: '8.3' nextcloud-versions: 'master' name: Nextcloud ${{ matrix.nextcloud-versions }} php${{ matrix.php-versions }} unit tests ${{ matrix.coverage && '(coverage)' || ''}} @@ -88,6 +90,10 @@ jobs: db: 'mysql' cache: 'redis' coverage: true + - php-versions: 8.4 + nextcloud-versions: 'stable34' + db: 'mysql' + cache: 'redis' - php-versions: 8.5 nextcloud-versions: 'master' db: 'mysql' From 0367a07118c904d5522a9047520104145ec7f219 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Tue, 19 May 2026 01:42:09 +0000 Subject: [PATCH 038/228] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- l10n/it.js | 250 ++++++++++++++++++++++++++++++++++++++++++++++-- l10n/it.json | 250 ++++++++++++++++++++++++++++++++++++++++++++++-- l10n/zh_TW.js | 17 ++++ l10n/zh_TW.json | 17 ++++ 4 files changed, 520 insertions(+), 14 deletions(-) diff --git a/l10n/it.js b/l10n/it.js index 0437fbecb3..3bab88b874 100644 --- a/l10n/it.js +++ b/l10n/it.js @@ -106,9 +106,9 @@ OC.L10N.register( "The folders to use for drafts, sent messages, deleted messages, archived messages and junk messages." : "Le cartelle da usare per bozze, messaggi inviati, messaggi archiviati e posta indesiderata.", "Automatic trash deletion" : "Eliminazione automatica del cestino", "Days after which messages in Trash will automatically be deleted:" : "Giorni dopo i quali i messaggi nel cestino verranno eliminati automaticamente:", - "Autoresponder" : "Risponditore automatico", + "Autoresponder" : "Risposta automatica", "Automated reply to incoming messages. If someone sends you several messages, this automated reply will be sent at most once every 4 days." : "Risposta automatica ai messaggi in arrivo. Se qualcuno ti invia più messaggi, questa risposta automatica verrà inviata al massimo una volta ogni 4 giorni.", - "The autoresponder uses Sieve, a scripting language supported by many email providers. If you're unsure whether yours does, check with your provider. If Sieve is available, click the button to go to the settings and enable it." : "Il risponditore automatico utilizza Sieve, un linguaggio di scripting supportato da molti provider di posta elettronica. Se non sei sicuro che il tuo lo supporti, verifica con il tuo provider. Se Sieve è disponibile, fai clic sul pulsante per accedere alle impostazioni e abilitarlo.", + "The autoresponder uses Sieve, a scripting language supported by many email providers. If you're unsure whether yours does, check with your provider. If Sieve is available, click the button to go to the settings and enable it." : "La risposta automatica utilizza Sieve, un linguaggio di scripting supportato da molti provider di posta elettronica. Se non sei sicuro che il tuo lo supporti, verifica con il tuo provider. Se Sieve è disponibile, fai clic sul pulsante per accedere alle impostazioni e abilitarlo.", "Go to Sieve settings" : "Vai alle impostazioni Sieve", "Calendar settings" : "Impostazioni Calendario", "Classification settings" : "Impostazioni di classificazione", @@ -130,6 +130,7 @@ OC.L10N.register( "Cancel" : "Annulla", "Search the body of messages in priority Inbox" : "Cerca nel corpo dei messaggi nella posta prioritaria", "Activate" : "Attiva", + "Make mails available to Context Chat" : "Rendi le email disponibili per Context Chat", "Remind about messages that require a reply but received none" : "Promemoria per i messaggi che richiedono una risposta ma non hanno ricevuto alcun riscontro", "Highlight external addresses" : "Evidenzia indirizzi esterni", "Could not update preference" : "Impossibile aggiornare la preferenza", @@ -174,6 +175,7 @@ OC.L10N.register( "Step 2" : "Passo 2", "Enable for the current domain" : "Abilita per il dominio corrente", "Assistance features" : "Funzionalità di assistenza", + "Context Chat integration" : "Integrazione con Context Chat", "Compose new message" : "Componi nuovo messaggio", "Newer message" : "Messaggio più recente", "Older message" : "Messaggio più datato", @@ -205,16 +207,23 @@ OC.L10N.register( "Encrypt with Mailvelope and send" : "Cifra con Mailvelope e invia", "Send later" : "Invia più tardi", "Message {id} could not be found" : "Il messaggio {id} non può essere trovato", + "Sign or Encrypt with S/MIME was selected, but we don't have a certificate for the selected alias. The message will not be signed or encrypted." : "Hai selezionato la firma o la cifratura con S/MIME, ma non è disponibile alcun certificato per l’alias scelto. Il messaggio non verrà firmato né cifrato.", + "Any existing formatting (for example bold, italic, underline or inline images) will be removed." : "La formattazione esistente (ad esempio grassetto, corsivo, sottolineato o immagini incorporate) verrà rimossa.", + "Turn off formatting" : "Disattiva formattazione", + "Turn off and remove formatting" : "Disattiva e rimuovi la formattazione", "Keep formatting" : "Mantieni la formattazione", "From" : "Da", "Select account" : "Seleziona account", "To" : "A", "Cc/Bcc" : "Cc/Ccn", + "Select recipient" : "Seleziona destinatario", + "Contact or email address …" : "Contatto o indirizzo email …", "Cc" : "Cc", "Bcc" : "Ccn", "Subject" : "Oggetto", "Subject …" : "Oggetto...", "This message came from a noreply address so your reply will probably not be read." : "Nota che il messaggio è arrivato da un indirizzo noreply, perciò la tua risposta non sarà probabilmente letta.", + "The following recipients do not have a S/MIME certificate: {recipients}." : "I seguenti destinatari non hanno un certificato S/MIME: {recipients}.", "The following recipients do not have a PGP key: {recipients}." : "I seguenti destinatari non hanno una chiave PGP: {recipients}.", "Write message …" : "Scrivi messaggio...", "Saving draft …" : "Salvataggio bozza…", @@ -226,6 +235,7 @@ OC.L10N.register( "Disable formatting" : "Disabilita la formattazione", "Upload attachment" : "Carica allegato", "Add attachment from Files" : "Aggiungi allegato da File", + "Add share link from Files" : "Aggiungi link condiviso da Files", "Smart picker" : "Selettore intelligente", "Request a read receipt" : "Richiedi una conferma di lettura", "Sign message with S/MIME" : "Firma il messaggio con S/MIME", @@ -246,7 +256,24 @@ OC.L10N.register( "Expand composer" : "Espandi compositore", "Close composer" : "Chiudi compositore", "Confirm" : "Conferma", + "Delegate access" : "Accesso delegato", "Revoke" : "Revoca", + "{userId} will no longer be able to act on your behalf" : "{userId} non potrà più agire per tuo conto", + "Could not fetch delegates" : "Non è stato possibile recuperare i delegati", + "Delegated access to {userId}" : "Accesso delegato a {userId}", + "Could not delegate access" : "Non è stato possibile delegare l’accesso", + "Revoked access for {userId}" : "Revoca l'accesso a {userId}", + "Could not revoke delegation" : "Non è possibile revocare la delega", + "Delegation" : "Delega", + "Allow users to send, receive, and delete mail on your behalf" : "Consenti agli utenti delegati di inviare, ricevere ed eliminare email per tuo conto", + "Revoke access" : "Revoca l'accesso", + "Add delegate" : "Aggiungi delegato", + "Select a user" : "Seleziona un utente", + "They will be able to send, receive, and delete mail on your behalf" : "Potranno inviare, ricevere ed eliminare email per tuo conto", + "Revoke access?" : "Revocare l'accesso?", + "Tag: {name} deleted" : "Etichetta: {name} eliminata", + "An error occurred, unable to delete the tag." : "Si è verificato un errore: non è stato possibile eliminare l’etichetta.", + "The tag will be deleted from all messages." : "L’etichetta verrà rimossa da tutti i messaggi.", "Plain text" : "Testo semplice", "Rich text" : "Testo formattato", "No messages in this folder" : "Nessun messaggio in questa cartella", @@ -262,9 +289,18 @@ OC.L10N.register( "Set reminder for this weekend" : "Imposta promemoria per questo fine settimana", "Next week – {timeLocale}" : "Prossima settimana – {timeLocale}", "Set reminder for next week" : "Imposta promemoria per la prossima settimana", + "Could not apply tag, configured tag not found" : "Non è stato possibile applicare l’etichetta: l’etichetta configurata non è stata trovata", + "Could not move thread, destination mailbox not found" : "Non è stato possibile spostare la conversazione: la cartella di destinazione non è stata trovata", + "Could not execute quick action" : "Non è stato possibile eseguire l’azione rapida", "Quick action executed" : "Azione rapida eseguita", + "No trash folder configured" : "Nessuna cartella Cestino configurata", "Could not delete message" : "Impossibile eliminare il messaggio", "Could not archive message" : "Impossibile archiviare il messaggio", + "Thread was snoozed" : "Conversazione posticipata", + "Could not snooze thread" : "Non è possibile posticipare la conversazione", + "Thread was unsnoozed" : "Posicipo della conversazione annullato", + "Could not unsnooze thread" : "Non è stato possibile annullare il posticipo della conversazione", + "This summary was AI generated" : "Questo riepilogo è stato generato tramite IA", "Encrypted message" : "Messaggio cifrato", "This message is unread" : "Questo messaggio è non letto", "Unfavorite" : "Rimuovi preferito", @@ -275,6 +311,8 @@ OC.L10N.register( "Mark not spam" : "Marca come posta non indesiderata", "Mark as spam" : "Marca come posta indesiderata", "Edit tags" : "Modifica etichette", + "Snooze" : "Posticipa", + "Unsnooze" : "Annulla posticipo", "Move thread" : "Sposta conversazione", "Move Message" : "Sposta messaggio", "Archive thread" : "Archivia la discussione", @@ -283,7 +321,9 @@ OC.L10N.register( "Delete message" : "Elimina messaggio", "More actions" : "Altre azioni", "Back" : "Indietro", + "Set custom snooze" : "Imposta posticipo personalizzato", "Edit as new message" : "Modifica come nuovo messaggio", + "Reply with meeting" : "Rispondi proponendo una riunione", "Create task" : "Crea attività", "Download message" : "Scarica messaggio", "Back to all actions" : "Torna a tutte le azioni", @@ -296,16 +336,22 @@ OC.L10N.register( "_Unfavorite {number}_::_Unfavorite {number}_" : ["Rimuovi dai preferiti {number}","Rimuovi dai preferiti {number}","Rimuovi dai preferiti {number}"], "_Favorite {number}_::_Favorite {number}_" : ["Aggiungi ai preferiti {number}","Aggiungi ai preferiti {number}","Aggiungi ai preferiti {number}"], "_Unselect {number}_::_Unselect {number}_" : ["Deseleziona {number}","Deseleziona {number}","Deseleziona {number}"], + "_Mark {number} as spam_::_Mark {number} as spam_" : ["Segna {number} come spam","Segna {number} come spam","Segna {number} come spam"], + "_Mark {number} as not spam_::_Mark {number} as not spam_" : ["Segna {number} come non spam","Segna {number} come non spam","Segna {number} come non spam"], + "_Edit tags for {number}_::_Edit tags for {number}_" : ["Modifica etichetta per {number}","Modifica etichette per {number}","Modifica etichette per {number}"], "_Move {number} thread_::_Move {number} threads_" : ["Sposta {number} conversazione","Sposta {number} conversazioni","Sposta {number} conversazioni"], "_Forward {number} as attachment_::_Forward {number} as attachment_" : ["Inoltra {number} come allegato","Inoltra {number} come allegati","Inoltra {number} come allegati"], "Mark as unread" : "Segna come non letto", "Mark as read" : "Segna come letto", + "Mark as unimportant" : "Segna come non importante", + "Mark as important" : "Segna come importante", "Report this bug" : "Segnala questo bug", "Event created" : "Evento creato", "Could not create event" : "Impossibile creare l'evento", "Create event" : "Crea evento", "All day" : "Tutto il giorno", "Attendees" : "Partecipanti", + "You can only invite attendees if your account has an email address set" : "Puoi invitare partecipanti solo se il tuo account ha un indirizzo email configurato", "Select calendar" : "Seleziona calendario", "Description" : "Descrizione", "Create" : "Crea", @@ -328,43 +374,79 @@ OC.L10N.register( "Decline" : "Rifiuta", "Tentatively accept" : "Accetta provvisoriamente", "More options" : "Altre opzioni", + "This message has an attached invitation but the invitation dates are in the past" : "Questo messaggio contiene un invito, ma le date dell’invito sono già passate.", + "This message has an attached invitation but the invitation does not contain a participant that matches any configured mail account address" : "Questo messaggio contiene un invito, ma nell’invito non è presente alcun partecipante associato agli indirizzi email configurati.", + "Could not remove internal address {sender}" : "Non è stato possibile rimuovere l’indirizzo interno {sender}", + "Could not add internal address {address}" : "Non è stato possibile aggiungere l’indirizzo interno {address}", "individual" : "individuale", "domain" : "dominio", "Remove" : "Rimuovi", "Add internal address" : "Aggiungi indirizzo interno", + "Add internal email or domain" : "Aggiungi indirizzo email o dominio interno", "Itinerary for {type} is not supported yet" : "L'itinerario per {type} non è ancora supportato", + "To archive a message please configure an archive folder in account settings" : "Per archiviare un messaggio, configura prima una cartella Archivio nelle impostazioni dell’account", "You are not allowed to move this message to the archive folder and/or delete this message from the current folder" : "Non ti è consentito spostare questo messaggio nella cartella degli archivi e/o eliminare questo messaggio dalla cartella corrente.", + "Your IMAP server does not support storing the seen/unseen state." : "Il server IMAP non supporta il salvataggio dello stato letto/non letto.", + "Could not mark message as seen/unseen" : "Non è stato possibile segnare il messaggio come letto/non letto", "Last hour" : "Ultima ora", "Today" : "Oggi", "Yesterday" : "Ieri", "Last week" : "Ultima settimana", "Last month" : "Ultimo mese", + "Could not open folder" : "Non è possibile aprire la cartella", + "Loading messages …" : "Caricamento messaggi in corso …", + "Indexing your messages. This can take a bit longer for larger folders." : "Indicizzazione dei messaggi in corso. Per le cartelle più grandi l’operazione potrebbe richiedere più tempo.", "Choose target folder" : "Scegli la cartella di destinazione", "No more submailboxes in here" : "Qui non ci sono altre sotto-caselle di posta", + "Messages will automatically be marked as important using AI. The system learns from which messages you interact with or mark as important. In the beginning you might have to manually change the importance to teach it, but it will improve over time" : "I messaggi verranno contrassegnati automaticamente come importanti tramite l’IA. Il sistema impara dai messaggi con cui interagisci o che segni come importanti. All’inizio potresti dover modificare manualmente il livello di importanza per addestrarlo, ma la precisione migliorerà nel tempo.", + "Messages that you marked as favorite will be shown at the top of folders. You can disable this behavior in the app settings" : "I messaggi che hai aggiunto ai preferiti verranno mostrati in cima alle cartelle. Puoi disattivare questa opzione dalle impostazioni di Posta.", + "AI identifies messages sent by you that likely require a reply but did not receive one after a couple of days and shows them here" : "L’IA identifica i messaggi inviati da te che probabilmente richiedono una risposta ma che non ne hanno ricevuta dopo alcuni giorni, e li mostra qui.", "Favorites" : "Preferiti", + "Favorites info" : "Informazioni sui preferiti", + "Load more favorites" : "Carica altri preferiti", + "Follow up" : "Follow-up", + "Follow up info" : "Informazioni sui follow-up", + "Load more follow ups" : "Carica altri follow-up", "Important info" : "Informazione importante", + "Load more important messages" : "Carica altri messaggi importanti", "Other" : "Altro", + "Load more other messages" : "Carica altri messaggi", "Could not send mdn" : "Impossibile inviare mdn", "The sender of this message has asked to be notified when you read this message." : "Il mittente di questo messaggio ha chiesto di essere avvisato quando leggi questo messaggio.", "Notify the sender" : "Notifica al mittente", "You sent a read confirmation to the sender of this message." : "Hai inviato una conferma di lettura al mittente di questo messaggio.", + "Message was snoozed" : "Messaggio posticipato", + "Could not snooze message" : "Non è possibile posticipare il messaggio", + "Message was unsnoozed" : "Posticipo del messaggio annullato", + "Could not unsnooze message" : "Non è stato possibile annullare il posticipo del messaggio", "Forward" : "Inoltra", "Move message" : "Sposta messaggio", "Translate" : "Traduci", + "Forward message as attachment" : "Inoltra il messaggio come allegato", "View source" : "Visualizza sorgente", "Print message" : "Stampa messaggio", + "Create mail filter" : "Crea filtro email", "Download thread data for debugging" : "Scarica i dati del conversazione per il debug", + "Suggested replies are using AI" : "Le risposte suggerite sono generate tramite IA", "Message body" : "Corpo del messaggio", + "Warning: The S/MIME signature of this message is unverified. The sender might be impersonating someone!" : "Attenzione: la firma S/MIME di questo messaggio non è verificata. Il mittente potrebbe non essere chi dichiara di essere.", + "AI info" : "IA informazioni", "Unnamed" : "Senza nome", "Embedded message" : "Messaggio incorporato", + "Attachment saved to Files" : "Allegato salvato in Files", + "Attachment could not be saved" : "L'allegato non può essere salvato", "calendar imported" : "calendario importato", "Choose a folder to store the attachment in" : "Scegli una cartella dove salvare l'allegato", "Import into calendar" : "Importa nel calendario", "Download attachment" : "Scarica allegato", "Save to Files" : "Salva in File", + "Attachments saved to Files" : "Allegati salvati in Files", + "Error while saving attachments" : "Errore durante il salvataggio degli allegati", + "View fewer attachments" : "Mostra meno allegati", "Choose a folder to store the attachments in" : "Scegli una cartella in cui salvare gli allegati", "Save all to Files" : "Salva tutto in File", "Download Zip" : "Scarica Zip", + "_View {count} more attachment_::_View {count} more attachments_" : ["Mostra altri {count} allegati","Mostra altri {count} allegati","Mostra altri {count} allegati"], "This message is encrypted with PGP. Install Mailvelope to decrypt it." : "Questo messaggio è cifrato con PGP. Installa Mailvelope per decifrarlo.", "The images have been blocked to protect your privacy." : "Le immagini sono state bloccate per proteggere la tua riservatezza.", "Show images" : "Mostra le immagini", @@ -377,11 +459,18 @@ OC.L10N.register( "Moving" : "Spostamento", "Moving thread" : "Spostamento conversazione", "Moving message" : "Spostamento messaggio", + "This account cannot connect" : "Questo account non riesce a connettersi", + "Connection failed. Please verify your information and try again" : "Connessione non riuscita. Verifica i dati inseriti e riprova.", "Used quota: {quota}% ({limit})" : "Spazio utilizzato: {quota} % ({limit})", "Used quota: {quota}%" : "Spazio utilizzato: {quota}%", + "Unable to create mailbox. The name likely contains invalid characters. Please try another name." : "Non è stato possibile creare la cartella. Il nome contiene probabilmente caratteri non validi. Prova a usare un altro nome.", "Remove account" : "Rimuovi account", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "L'account per {email} e i dati di posta elettronica memorizzati nella cache saranno rimossi da Nextcloud, ma non dal tuo fornitore di posta elettronica.", "Remove {email}" : "Rimuovi {email}", + "could not delete account" : "Non è stato possibile eliminare l’account", + "Provisioned account is disabled" : "L’account configurato automaticamente è disattivato", + "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "Accedi con password per abilitare questo account. La sessione corrente usa un accesso senza password, ad esempio SSO o WebAuthn.", + "Delegate account" : "Account delegato", "Show only subscribed folders" : "Mostra solo le cartelle sottoscritte", "Add folder" : "Aggiungi cartella", "Folder name" : "Nome della cartella", @@ -394,19 +483,26 @@ OC.L10N.register( "_{total} message_::_{total} messages_" : ["{total} messaggio","{total} messaggi","{total} messaggi"], "_{unread} unread of {total}_::_{unread} unread of {total}_" : ["{unread} non letto di {total}","{unread} non letti di {total}","{unread} non letti di {total}"], "Loading …" : "Caricamento in corso...", + "All messages in mailbox will be deleted." : "Tutti i messaggi presenti nella cartella verranno eliminati.", + "Clear mailbox {name}" : "Svuota cartella {name}", + "Clear folder" : "Svuota cartella", "The folder and all messages in it will be deleted." : "La cartella e tutti i messaggi in essa saranno eliminati.", "Delete folder" : "Elimina cartella", "Delete folder {name}" : "Elimina cartella {name}", "An error occurred, unable to rename the mailbox." : "Si è verificato un errore, impossibile rinominare la casella di posta.", + "Please wait 10 minutes before repairing again" : "Attendi 10 minuti prima di ripetere la riparazione", "Mark all as read" : "Marca tutti come letti", "Add subfolder" : "Aggiungi sottocartella", "Rename" : "Rinomina", "Move folder" : "Sposta cartella", + "Repair folder" : "Ripara cartella", "Clear cache" : "Svuota cache", "Clear locally cached data, in case there are issues with synchronization." : "Cancella i dati locali in cache, nel caso ci siano problemi di sincronizzazione.", "Subscribed" : "Sottoscritta", "Sync in background" : "Sincronizza in background", + "Delete all messages" : "Elimina tutti i messaggi", "Outbox" : "Posta in uscita", + "Translate this message to {language}" : "Traduci questo messaggio in {language}", "New message" : "Nuovo messaggio", "Edit message" : "Modifica messaggio", "Draft" : "Bozza", @@ -416,36 +512,59 @@ OC.L10N.register( "Failed to save draft" : "Impossibile salvare la bozza", "attachment" : "allegato", "attached" : "allegato", + "No \"sent\" folder configured. Please pick one in the account settings." : "Nessuna cartella “Posta inviata” configurata. Selezionane una nelle impostazioni dell’account.", "You are trying to send to many recipients in To and/or Cc. Consider using Bcc to hide recipient addresses." : "Stai tentando di inviare a troppi destinatari in A e/o CC. Considera l'idea di utilizzare CCN per nascondere gli indirizzi dei destinatari.", "Your message has no subject. Do you want to send it anyway?" : "Il tuo messaggio non ha oggetto. Vuoi inviarlo comunque?", "You mentioned an attachment. Did you forget to add it?" : "Hai menzionato un allegato. Hai dimenticato di aggiungerlo?", "Message discarded" : "Messaggio scartato", "Could not discard message" : "Impossibile eliminare il messaggio", + "Maximize composer" : "Espandi finestra", + "Show recipient details" : "Mostra dettagli dei destinatari", + "Hide recipient details" : "Nascondi dettagli dei destinatari", + "Minimize composer" : "Riduci finestra", "Error sending your message" : "Errore di invio del messaggio", "Retry" : "Riprova", "Warning sending your message" : "Avviso durante l'invio del tuo messaggio", "Send anyway" : "Invia comunque", - "Autoresponder off" : "Risponditore automatico spento", - "Autoresponder on" : "Risponditore automatico attivo", + "Welcome to {productName} Mail" : "Benvenuto in {productName} Mail", + "Start writing a message by clicking below or select an existing message to display its contents" : "Inizia a scrivere un messaggio cliccando qui sotto oppure seleziona un messaggio esistente per visualizzarne il contenuto", + "Autoresponder off" : "Risposta automatica disattivata", + "Autoresponder on" : "Risposta automatica attiva", + "Autoresponder follows system settings" : "La risposta automatica segue le impostazioni di sistema", + "The autoresponder follows your personal absence period settings." : "La risposta automatica segue le impostazioni del tuo periodo di assenza.", + "Edit absence settings" : "Modifica impostazioni di assenza", "First day" : "Primo giorno", "Last day (optional)" : "Ultimo giorno (facoltativo)", + "${subject} will be replaced with the subject of the message you are responding to" : "${subject} verrà sostituito con l’oggetto del messaggio a cui stai rispondendo", "Message" : "Messaggio", "Oh Snap!" : "Accidenti!", - "Save autoresponder" : "Salva risponditore automatico", + "Save autoresponder" : "Salva risposta automatica", "Could not open outbox" : "Impossibile aprire la posta in uscita", + "Pending or not sent messages will show up here" : "Qui verranno mostrati i messaggi in sospeso o non ancora inviati", + "Could not copy to \"Sent\" folder" : "Non è possibile copiare nella cartella \"Posta inviata\"", + "Mail server error" : "Errore del server di posta", "Message could not be sent" : "Il messaggio non può essere inviato", "Message deleted" : "Messaggio eliminato", + "Copy to \"Sent\" Folder" : "Copia nella cartella \"Posta inviata\"", + "Copy to Sent Folder" : "Copia nella cartella Posta inviata", + "Phishing email" : "Email di phishing", + "This email might be a phishing attempt" : "Questa email potrebbe essere un tentativo di phishing", + "Hide suspicious links" : "Nascondi link sospetti", + "Show suspicious links" : "Mostra link sospetti", + "link text" : "testo del link", "Copied email address to clipboard" : "Indirizzo email copiato negli appunti", "Could not copy email address to clipboard" : "Impossibile copiare l'indirizzo email negli appunti", "Contacts with this address" : "Contatti con questo indirizzo", "Add to Contact" : "Aggiungi ai contatti", "New Contact" : "Nuovo contatto", "Copy to clipboard" : "Copia negli appunti", + "Contact name …" : "Nome contatto …", "Add" : "Aggiungi", "Show less" : "Mostra meno", "Show more" : "Mostra più", "Clear" : "Pulisci", "Search in folder" : "Cerca nella cartella", + "Open search modal" : "Apri finestra di ricerca", "Close" : "Chiudi", "Search parameters" : "Parametri di ricerca", "Search subject" : "Cerca nell'oggetto", @@ -460,9 +579,13 @@ OC.L10N.register( "Select BCC recipients" : "Seleziona i destinatari CCN", "Tags" : "Etichette", "Select tags" : "Seleziona etichette", + "Marked as" : "Contrassegnato come", "Has attachments" : "Ha allegati", + "Mentions me" : "Mi menziona", + "Has attachment" : "Con allegato", + "To me" : "A me", "Enable mail body search" : "Abilita ricerca nel corpo del messaggio", - "Sieve is a powerful language for writing filters for your mailbox. You can manage the sieve scripts in Mail if your email service supports it. Sieve is also required to use Autoresponder and Filters." : "Sieve è un potente linguaggio per la creazione di filtri per la tua casella di posta. Puoi gestire gli script Sieve nell’app Posta se il tuo servizio email lo supporta. Sieve è inoltre necessario per utilizzare il risponditore automatico e i filtri.", + "Sieve is a powerful language for writing filters for your mailbox. You can manage the sieve scripts in Mail if your email service supports it. Sieve is also required to use Autoresponder and Filters." : "Sieve è un potente linguaggio per la creazione di filtri per la tua casella di posta. Puoi gestire gli script Sieve nell’app Posta se il tuo servizio email lo supporta. Sieve è inoltre necessario per utilizzare le risposte automatiche e i filtri.", "Enable sieve filter" : "Abilita filtri Sieve", "Sieve host" : "Host Sieve", "Sieve security" : "Sicurezza Sieve", @@ -472,33 +595,67 @@ OC.L10N.register( "Custom" : "Personalizzato", "Sieve User" : "Utente Sieve", "Sieve Password" : "Password Sieve", + "Oh snap!" : "Ops!", "Save sieve settings" : "Salva impostazione Sieve", + "The syntax seems to be incorrect:" : "La sintassi non sembra corretta:", "Save sieve script" : "Salva script Sieve", "Signature …" : "Scrivi firma …", + "Your signature is larger than 2 MB. This may affect the performance of your editor." : "La firma supera i 2 MB e potrebbe rallentare l’editor.", "Save signature" : "Salva firma", "Place signature above quoted text" : "Posiziona la firma sopra il testo citato", "Message source" : "Sorgente del messaggio", "An error occurred, unable to rename the tag." : "Si è verificato un errore, impossibile rinominare l'etichetta.", "Edit name or color" : "Modifica nome o colore", + "Saving new tag name …" : "Salvataggio del nuovo nome dell’etichetta in corso …", "Delete tag" : "Elimina etichetta", + "Set tag" : "Assegna etichetta", + "Unset tag" : "Rimuovi etichetta", + "Tag name is a hidden system tag" : "Questo nome è riservato a un’etichetta di sistema nascosta", "Tag already exists" : "L'etichetta esiste già", + "Tag name cannot be empty" : "Il nome dell’etichetta non può essere vuoto", "An error occurred, unable to create the tag." : "Si è verificato un errore, impossibile creare l'etichetta.", "Add default tags" : "Aggiungi etichette predefiniti", "Add tag" : "Aggiungi etichetta", + "Saving tag …" : "Salvataggio etichetta in corso …", "Task created" : "Attività creata", "Could not create task" : "Impossibile creare l'attività", + "No calendars with task list support" : "Nessun calendario supporta le liste di attività", + "Summarizing thread failed." : "Non è stato possibile generare il riepilogo della conversazione.", "Could not load your message thread" : "Impossibile caricare la tua discussione", + "The thread doesn't exist or has been deleted" : "La conversazione non esiste oppure è stata eliminata", + "Email was not able to be opened" : "Non è stato possibile aprire l’email", "Print" : "Stampa", "Could not print message" : "Impossibile stampare il messaggio", + "Loading thread" : "Caricamento conversazione in corso", "Not found" : "Non trovato", "Encrypted & verified " : "Cifrato e verificato", "Signature verified" : "Firma verificata", "Signature unverified " : "Firma non verificata", "This message was encrypted by the sender before it was sent." : "Questo messaggio è stato cifrato dal mittente prima di essere inviato.", + "This message contains a verified digital S/MIME signature. The message wasn't changed since it was sent." : "Questo messaggio contiene una firma digitale S/MIME verificata. Il messaggio non è stato modificato dopo l’invio.", + "This message contains an unverified digital S/MIME signature. The message might have been changed since it was sent or the certificate of the signer is untrusted." : "Questo messaggio contiene una firma digitale S/MIME non verificata. Il messaggio potrebbe essere stato modificato dopo l’invio oppure il certificato del firmatario potrebbe non essere attendibile.", "Reply all" : "Rispondi a tutti", + "Unsubscribe request sent" : "Richiesta di disiscrizione inviata", + "Could not unsubscribe from mailing list" : "Non è stato possibile disiscriversi dalla mailing list", + "Please wait for the message to load" : "Attendi il caricamento del messaggio", + "Disable reminder" : "Disattiva promemoria", "Unsubscribe" : "Rimuovi sottoscrizione", "Reply to sender only" : "Rispondi solo al mittente", + "Mark as unfavorite" : "Rimuovi dai preferiti", + "Mark as favorite" : "Aggiungi ai preferiti", + "To:" : "A: ", + "Cc:" : "Cc:", + "Bcc:" : "Bcc:", + "Unsubscribe via link" : "Disiscriviti tramite link", + "Unsubscribing will stop all messages from the mailing list {sender}" : "Disiscrivendoti non riceverai più messaggi da questa mailing list {sender}", + "Send unsubscribe email" : "Invia email di disiscrizione", + "Unsubscribe via email" : "Disiscriviti tramite email", + "{name} Assistant" : "{name} Assistente", + "Thread summary" : "Riepilogo della conversazione", "Go to latest message" : "Vai all'ultimo messaggio", + "Newest message" : "Messaggio più recente", + "This summary is AI generated and may contain mistakes." : "Questo riepilogo è stato generato tramite IA e potrebbe contenere imprecisioni.", + "Please select languages to translate to and from" : "Seleziona la lingua di partenza e quella di destinazione per la traduzione", "The message could not be translated" : "Questo messaggio non può essere tradotto", "Translation copied to clipboard" : "Traduzione copiata negli appunti", "Translation could not be copied" : "La traduzione non può essere copiata", @@ -508,6 +665,7 @@ OC.L10N.register( "Target language to translate into" : "Lingua di destinazione in cui tradurre", "Translate to" : "Traduci in", "Translating" : "Traduco", + "This translation is generated using AI and may contain inaccuracies" : "Questa traduzione è stata generata con l’IA e potrebbe contenere imprecisioni", "Copy translated text" : "Copia testo tradotto", "Disable trash retention by leaving the field empty or setting it to 0. Only mails deleted after enabling trash retention will be processed." : "Disattiva la conservazione nel cestino lasciando il campo vuoto o impostandolo a 0. Verranno elaborati solo i messaggi eliminati dopo l’attivazione della conservazione.", "Could not remove trusted sender {sender}" : "Impossibile rimuovere il mittente fidato {sender}", @@ -522,15 +680,48 @@ OC.L10N.register( "{trainNr} from {depStation} to {arrStation}" : "{trainNr} da {depStation} a {arrStation}", "Train from {depStation} to {arrStation}" : "Treno da {depStation} a {arrStation}", "Train" : "Treno", + "Mark message as" : "Contrassegna il messaggio come", + "Add flag" : "Aggiungi contrassegno", "Move into folder" : "Sposta nella cartella", "Stop" : "Ferma", + "Delete action" : "Elimina azione", + "Answered" : "Risposto", "Deleted" : "Eliminato", + "Flagged" : "Contrassegnato", + "Seen" : "Letto", + "Enter flag" : "Inserisci contrassegno", + "Stop ends all processing" : "Interrompi: termina tutte le elaborazioni", + "Sender" : "Mittente", "Recipient" : "Destinatario", + "Create a new mail filter" : "Crea un nuovo filtro email", + "Choose the headers you want to use to create your filter. In the next step, you will be able to refine the filter conditions and specify the actions to be taken on messages that match your criteria." : "Scegli le intestazioni da usare per creare il filtro. Nel passaggio successivo potrai definire meglio le condizioni e indicare quali azioni applicare ai messaggi che corrispondono ai criteri scelti.", "Delete filter" : "Elimina filtro", + "Delete mail filter {filterName}?" : "Cancella filtro email {filterName}?", + "Are you sure to delete the mail filter?" : "Sei sicuro di cancellare il filtro email?", + "New filter" : "Nuovo filtro", + "Filter saved" : "Filtro salvato", + "Could not save filter" : "Impossibile salvare il filtro", + "Filter deleted" : "Filtro cancellato", + "Could not delete filter" : "Impossibile cancellare il filtro", + "Take control of your email chaos. Filters help you to prioritize what matters and eliminate clutter." : "Metti ordine nella tua posta. I filtri ti aiutano a dare priorità ai messaggi importanti e a ridurre il superfluo.", + "Hang tight while the filters load" : "Caricamento dei filtri in corso", + "Filter is active" : "Il filtro è attivo", + "Filter is not active" : "Il filtro non è attivo", + "If all the conditions are met, the actions will be performed" : "Se tutte le condizioni sono soddisfatte, verranno eseguite le azioni previste", + "If any of the conditions are met, the actions will be performed" : "Se almeno una delle condizioni è soddisfatta, verranno eseguite le azioni previste", "Help" : "Assistenza", "contains" : "contiene", + "A substring match. The field matches if the provided value is contained within it. For example, \"report\" would match \"port\"." : "Corrispondenza parziale. Il campo corrisponde se contiene il valore indicato. Ad esempio, “report” può corrispondere a “Business report 2024”.", "matches" : "corrisponde", + "A pattern match using wildcards. The \"*\" symbol represents any number of characters (including none), while \"?\" represents exactly one character. For example, \"*report*\" would match \"Business report 2024\"." : "Corrispondenza tramite caratteri jolly. Il simbolo “*” rappresenta qualsiasi numero di caratteri (anche nessuno), mentre “?” rappresenta esattamente un carattere. Ad esempio, “*report*” può corrispondere a “Business report 2024”.", + "Enter subject" : "Inserisci oggetto", + "Enter sender" : "Inserisci mittente", + "Enter recipient" : "Inserisci destinatario", + "is exactly" : "è esattamente", + "Conditions" : "Condizioni", + "Add condition" : "Aggiungi condizione", "Actions" : "Azioni", + "Add action" : "Aggiungi azione", "Priority" : "Priorità", "Enable filter" : "Abilita filtri", "Tag" : "Etichetta", @@ -538,11 +729,19 @@ OC.L10N.register( "Edit quick action" : "Modifica azione rapida", "Add quick action" : "Aggiungi azione rapida", "Quick action deleted" : "Azione rapida eliminata", + "Failed to delete quick action" : "Non è stato possibile eliminare l’azione rapida", + "Failed to update quick action" : "Non è stato possibile aggiornare l’azione rapida", + "Failed to update step in quick action" : "Non è stato possibile aggiornare il passaggio dell’azione rapida", "Quick action updated" : "Azione rapida aggiornata", + "Failed to create quick action" : "Non è stato possibile creare l’azione rapida", + "Failed to add steps to quick action" : "Non è stato possibile aggiungere passaggi all’azione rapida", "Quick action created" : "Azione rapida creata", + "Failed to delete action step" : "Non è stato possibile eliminare il passaggio dell’azione", "No quick actions yet." : "Nessuna azione rapida disponibile al momento.", "Edit" : "Modifica", "Quick action name" : "Azione rapida nome", + "Do the following actions" : "Esegui le seguenti azioni", + "Add another action" : "Aggiungi un’altra azione", "Successfully updated config for \"{domain}\"" : "Configurazione aggiornata con successo per \"{domain}\"", "Error saving config" : "Errore durante il salvataggio della configurazione", "Saved config for \"{domain}\"" : "Configurazione salvata per \"{domain}\"", @@ -551,6 +750,7 @@ OC.L10N.register( "There was an error when provisioning accounts." : "Si è verificato un errore durante il approvvigionamento degli account.", "Successfully deleted and deprovisioned accounts for \"{domain}\"" : " Account eliminati e approvvigionato correttamente per \"{domain}\"", "Error when deleting and deprovisioning accounts for \"{domain}\"" : "Errore durante l'eliminazione e il approvvigionamento degli account per \"{domain}\"", + "Could not save default classification setting" : "Non è stato possibile salvare l’impostazione di classificazione predefinita", "Mail app" : "Applicazione di posta", "The mail app allows users to read mails on their IMAP accounts." : "L'applicazione di posta consente agli utenti di leggere i messaggi dei propri account IMAP.", "Here you can find instance-wide settings. User specific settings are found in the app itself (bottom-left corner)." : "Qui puoi trovare le impostazioni a livello di istanza. Le impostazioni specifiche dell'utente si trovano nell'applicazione stessa (angolo in basso a sinistra).", @@ -566,11 +766,28 @@ OC.L10N.register( "Provisioning Configurations" : "Configurazione di approvvigionamento", "Add new config" : "Aggiungi nuova configurazione", "Provision all accounts" : "Predisponi tutti gli account", + "Allow additional mail accounts" : "Consenti account email aggiuntivi", + "Allow additional Mail accounts from User Settings" : "Consenti account Mail aggiuntivi dalle impostazioni utente", + "Enable text processing through LLMs" : "Abilita elaborazione del testo tramite LLM", + "The Mail app can process user data with the help of the configured large language model and provide assistance features like thread summaries, smart replies and event agendas." : "L’app Mail può elaborare i dati dell’utente tramite il modello linguistico configurato e offrire funzionalità avanzate come riepiloghi delle conversazioni, risposte intelligenti e agenda degli eventi.", + "Enable LLM processing" : "Abilita elaborazione tramite LLM", + "Enable classification by importance by default" : "Abilita di default la classificazione per importanza", + "The Mail app can classify incoming emails by importance using machine learning. This feature is enabled by default but can be disabled by default here. Individual users will still be able to toggle the feature for their accounts." : "L’app Mail può classificare automaticamente le email in arrivo in base alla loro importanza tramite machine learning. La funzionalità è attiva per impostazione predefinita, ma può essere disattivata qui. I singoli utenti potranno comunque abilitarla o disabilitarla per il proprio account.", + "Enable classification of important mails by default" : "Abilita di default la classificazione delle email importanti", "Anti Spam Service" : "Servizio anti-spam", "You can set up an anti spam service email address here." : "È possibile impostare un indirizzo e-mail del servizio Anti-Spam qui.", "Any email that is marked as spam will be sent to the anti spam service." : "Qualsiasi email contrassegnata come posta indesiderata verrà inviata al servizio anti-spam.", "Gmail integration" : "Integrazione Gmail", + "Gmail allows users to access their email via IMAP. For security reasons this access is only possible with an OAuth 2.0 connection or Google accounts that use two-factor authentication and app passwords." : "Gmail consente l’accesso alle email tramite IMAP. Per motivi di sicurezza, questa modalità è disponibile solo tramite una connessione OAuth 2.0 oppure per gli account Google con autenticazione a due fattori e password per le app.", + "You have to register a new Client ID for a \"Web application\" in the Google Cloud console. Add the URL {url} as authorized redirect URI." : "Devi registrare un nuovo Client ID di tipo “Applicazione web” nella console Google Cloud. Aggiungi l’URL {url} come URI di reindirizzamento autorizzato.", "Microsoft integration" : "Integrazione Microsoft", + "Microsoft requires you to access your emails via IMAP using OAuth 2.0 authentication. To do this, you need to register an app with Microsoft Entra ID, formerly known as Microsoft Azure Active Directory." : "Microsoft richiede l’accesso alle email tramite IMAP con autenticazione OAuth 2.0. Per configurarlo, devi registrare un’app in Microsoft Entra ID, in precedenza noto come Microsoft Azure Active Directory.", + "Redirect URI" : "URI di reindirizzamento", + "For more details, please click here to open our documentation." : "Per maggiori dettagli, fai clic qui per aprire la documentazione.", + "User Interface Preference Defaults" : "Preferenze predefinite dell’interfaccia utente", + "These settings are used to pre-configure the user interface preferences they can be overridden by the user in the mail settings" : "Queste impostazioni servono a preconfigurare le preferenze dell’interfaccia utente. L’utente potrà comunque modificarle dalle impostazioni di Posta.", + "Message View Mode" : "Modalità di visualizzazione dei messaggi", + "Show only the selected message" : "Mostra solo il messaggio selezionato", "Successfully set up anti spam email addresses" : "Impostare correttamente gli indirizzi e-mail anti-spam", "Error saving anti spam email addresses" : "Errore durante il salvataggio degli indirizzi email anti-spam", "Successfully deleted anti spam reporting email" : "Email di segnalazione anti-spam eliminata con successo", @@ -623,6 +840,7 @@ OC.L10N.register( "The provided PKCS #12 certificate must contain at least one certificate and exactly one private key." : "Il certificato PKCS #12 fornito deve contenere almeno un certificato ed esattamente una chiave privata.", "Failed to import the certificate. Please check the password." : "Impossibile importare il certificato. Controlla la password.", "Certificate imported successfully" : "Certificato importato correttamente", + "Failed to import the certificate. Please make sure that the private key matches the certificate and is not protected by a passphrase." : "Non è stato possibile importare il certificato. Verifica che la chiave privata corrisponda al certificato e che non sia protetta da passphrase.", "Failed to import the certificate" : "Importare del certificato non riuscita", "S/MIME certificates" : "Certificati S/MIME", "Certificate name" : "Nome del certificato", @@ -639,13 +857,24 @@ OC.L10N.register( "The private key is only required if you intend to send signed and encrypted emails using this certificate." : "La chiave privata è necessaria solo se si intende inviare email firmate e cifrate utilizzando questo certificato.", "Submit" : "Invia", "No text blocks available" : "Nessun blocco di testo disponibile", + "Text block deleted" : "Blocco di testo cancellato", + "Failed to delete text block" : "Cancellazione testo di blocco fallita", + "Text block shared with {sharee}" : "Blocco di testo condiviso con {sharee}", + "Failed to share text block with {sharee}" : "Non è stato possibile condividere il blocco di testo con {sharee}", + "Share deleted for {name}" : "Condivisione rimossa per {name}", + "Failed to delete share with {name}" : "Non è stato possibile rimuovere la condivisione con {name}", "Guest" : "Ospite", "Group" : "Gruppo", + "Failed to save text block" : "Salvataggio testo di blocco fallito", "Shared" : "Condiviso", "Edit {title}" : "Modifica {title}", "Delete {title}" : "Elimina {title}", + "Edit text block" : "Modifica blocco di testo", "Shares" : "Condivisioni", + "Search for users or groups" : "Cerca utenti o gruppi", + "Choose a text block to insert at the cursor" : "Scegli un blocco di testo da inserire nel punto selezionato", "Insert" : "Inserisci", + "Insert text block" : "Inserisci testo di blocco", "Account connected" : "Account connesso", "You can close this window" : "Puoi chiudere questa finestra", "Connect your mail account" : "Connetti il tuo account di posta", @@ -667,15 +896,22 @@ OC.L10N.register( "Discard changes" : "Scarta le modifiche", "Discard unsaved changes" : "Scartare le modifiche non salvate", "Keep editing message" : "Continua a modificare il messaggio", + "(All or part of this reply was generated by AI)" : "(Questa risposta è stata generata interamente o parzialmente dall'IA)", "Attachments were not copied. Please add them manually." : "Gli allegati non sono stati copiati. Aggiungili manualmente.", + "Could not create snooze mailbox" : "Non è stato possibile creare la cartella per i messaggi posticipati", + "Sorry, the message could not be loaded. The draft may no longer exist. Please refresh the page and try again." : "Non è stato possibile caricare il messaggio. La bozza potrebbe non esistere più. Aggiorna la pagina e riprova.", + "Sending message…" : "Invio del messaggio in corso...", "Message sent" : "Messaggio inviato", "Could not send message" : "Impossibile inviare il messaggio", + "Message copied to \"Sent\" folder" : "Messaggio copiato nella cartella “Posta inviata”", + "Could not copy message to \"Sent\" folder" : "Non è possibile copiare messaggi nella cartella \"Posta inviata\"", "Could not load {tag}{name}{endtag}" : "Impossibile caricare {tag}{name}{endtag}", "There was a problem loading {tag}{name}{endtag}" : "Si è verificato un programma durante il caricamento di {tag}{name}{endtag}", "Could not load your message" : "Impossibile caricare il tuo messaggio", "Could not load the desired message" : "Impossibile caricare messaggio desiderato", "Could not load the message" : "Impossibile caricare il messaggio", "Error loading message" : "Errore durante il caricamento del messaggio", - "Messages will automatically be marked as important based on which messages you interacted with or marked as important. In the beginning you might have to manually change the importance to teach the system, but it will improve over time." : "I messaggi saranno automaticamente contrassegnati come importanti in base ai messaggi con cui hai interagito o contrassegnati come importanti. All'inizio potresti dover cambiare manualmente l'importanza per addestrare il sistema, ma migliorerà nel tempo." + "Messages will automatically be marked as important based on which messages you interacted with or marked as important. In the beginning you might have to manually change the importance to teach the system, but it will improve over time." : "I messaggi saranno automaticamente contrassegnati come importanti in base ai messaggi con cui hai interagito o contrassegnati come importanti. All'inizio potresti dover cambiare manualmente l'importanza per addestrare il sistema, ma migliorerà nel tempo.", + "Messages sent by you that require a reply but did not receive one after a couple of days will be shown here." : "Qui verranno mostrati i messaggi che hai inviato e che richiedono una risposta, ma che non ne hanno ricevuta una dopo alcuni giorni." }, "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); diff --git a/l10n/it.json b/l10n/it.json index bd275310c6..f6928a3500 100644 --- a/l10n/it.json +++ b/l10n/it.json @@ -104,9 +104,9 @@ "The folders to use for drafts, sent messages, deleted messages, archived messages and junk messages." : "Le cartelle da usare per bozze, messaggi inviati, messaggi archiviati e posta indesiderata.", "Automatic trash deletion" : "Eliminazione automatica del cestino", "Days after which messages in Trash will automatically be deleted:" : "Giorni dopo i quali i messaggi nel cestino verranno eliminati automaticamente:", - "Autoresponder" : "Risponditore automatico", + "Autoresponder" : "Risposta automatica", "Automated reply to incoming messages. If someone sends you several messages, this automated reply will be sent at most once every 4 days." : "Risposta automatica ai messaggi in arrivo. Se qualcuno ti invia più messaggi, questa risposta automatica verrà inviata al massimo una volta ogni 4 giorni.", - "The autoresponder uses Sieve, a scripting language supported by many email providers. If you're unsure whether yours does, check with your provider. If Sieve is available, click the button to go to the settings and enable it." : "Il risponditore automatico utilizza Sieve, un linguaggio di scripting supportato da molti provider di posta elettronica. Se non sei sicuro che il tuo lo supporti, verifica con il tuo provider. Se Sieve è disponibile, fai clic sul pulsante per accedere alle impostazioni e abilitarlo.", + "The autoresponder uses Sieve, a scripting language supported by many email providers. If you're unsure whether yours does, check with your provider. If Sieve is available, click the button to go to the settings and enable it." : "La risposta automatica utilizza Sieve, un linguaggio di scripting supportato da molti provider di posta elettronica. Se non sei sicuro che il tuo lo supporti, verifica con il tuo provider. Se Sieve è disponibile, fai clic sul pulsante per accedere alle impostazioni e abilitarlo.", "Go to Sieve settings" : "Vai alle impostazioni Sieve", "Calendar settings" : "Impostazioni Calendario", "Classification settings" : "Impostazioni di classificazione", @@ -128,6 +128,7 @@ "Cancel" : "Annulla", "Search the body of messages in priority Inbox" : "Cerca nel corpo dei messaggi nella posta prioritaria", "Activate" : "Attiva", + "Make mails available to Context Chat" : "Rendi le email disponibili per Context Chat", "Remind about messages that require a reply but received none" : "Promemoria per i messaggi che richiedono una risposta ma non hanno ricevuto alcun riscontro", "Highlight external addresses" : "Evidenzia indirizzi esterni", "Could not update preference" : "Impossibile aggiornare la preferenza", @@ -172,6 +173,7 @@ "Step 2" : "Passo 2", "Enable for the current domain" : "Abilita per il dominio corrente", "Assistance features" : "Funzionalità di assistenza", + "Context Chat integration" : "Integrazione con Context Chat", "Compose new message" : "Componi nuovo messaggio", "Newer message" : "Messaggio più recente", "Older message" : "Messaggio più datato", @@ -203,16 +205,23 @@ "Encrypt with Mailvelope and send" : "Cifra con Mailvelope e invia", "Send later" : "Invia più tardi", "Message {id} could not be found" : "Il messaggio {id} non può essere trovato", + "Sign or Encrypt with S/MIME was selected, but we don't have a certificate for the selected alias. The message will not be signed or encrypted." : "Hai selezionato la firma o la cifratura con S/MIME, ma non è disponibile alcun certificato per l’alias scelto. Il messaggio non verrà firmato né cifrato.", + "Any existing formatting (for example bold, italic, underline or inline images) will be removed." : "La formattazione esistente (ad esempio grassetto, corsivo, sottolineato o immagini incorporate) verrà rimossa.", + "Turn off formatting" : "Disattiva formattazione", + "Turn off and remove formatting" : "Disattiva e rimuovi la formattazione", "Keep formatting" : "Mantieni la formattazione", "From" : "Da", "Select account" : "Seleziona account", "To" : "A", "Cc/Bcc" : "Cc/Ccn", + "Select recipient" : "Seleziona destinatario", + "Contact or email address …" : "Contatto o indirizzo email …", "Cc" : "Cc", "Bcc" : "Ccn", "Subject" : "Oggetto", "Subject …" : "Oggetto...", "This message came from a noreply address so your reply will probably not be read." : "Nota che il messaggio è arrivato da un indirizzo noreply, perciò la tua risposta non sarà probabilmente letta.", + "The following recipients do not have a S/MIME certificate: {recipients}." : "I seguenti destinatari non hanno un certificato S/MIME: {recipients}.", "The following recipients do not have a PGP key: {recipients}." : "I seguenti destinatari non hanno una chiave PGP: {recipients}.", "Write message …" : "Scrivi messaggio...", "Saving draft …" : "Salvataggio bozza…", @@ -224,6 +233,7 @@ "Disable formatting" : "Disabilita la formattazione", "Upload attachment" : "Carica allegato", "Add attachment from Files" : "Aggiungi allegato da File", + "Add share link from Files" : "Aggiungi link condiviso da Files", "Smart picker" : "Selettore intelligente", "Request a read receipt" : "Richiedi una conferma di lettura", "Sign message with S/MIME" : "Firma il messaggio con S/MIME", @@ -244,7 +254,24 @@ "Expand composer" : "Espandi compositore", "Close composer" : "Chiudi compositore", "Confirm" : "Conferma", + "Delegate access" : "Accesso delegato", "Revoke" : "Revoca", + "{userId} will no longer be able to act on your behalf" : "{userId} non potrà più agire per tuo conto", + "Could not fetch delegates" : "Non è stato possibile recuperare i delegati", + "Delegated access to {userId}" : "Accesso delegato a {userId}", + "Could not delegate access" : "Non è stato possibile delegare l’accesso", + "Revoked access for {userId}" : "Revoca l'accesso a {userId}", + "Could not revoke delegation" : "Non è possibile revocare la delega", + "Delegation" : "Delega", + "Allow users to send, receive, and delete mail on your behalf" : "Consenti agli utenti delegati di inviare, ricevere ed eliminare email per tuo conto", + "Revoke access" : "Revoca l'accesso", + "Add delegate" : "Aggiungi delegato", + "Select a user" : "Seleziona un utente", + "They will be able to send, receive, and delete mail on your behalf" : "Potranno inviare, ricevere ed eliminare email per tuo conto", + "Revoke access?" : "Revocare l'accesso?", + "Tag: {name} deleted" : "Etichetta: {name} eliminata", + "An error occurred, unable to delete the tag." : "Si è verificato un errore: non è stato possibile eliminare l’etichetta.", + "The tag will be deleted from all messages." : "L’etichetta verrà rimossa da tutti i messaggi.", "Plain text" : "Testo semplice", "Rich text" : "Testo formattato", "No messages in this folder" : "Nessun messaggio in questa cartella", @@ -260,9 +287,18 @@ "Set reminder for this weekend" : "Imposta promemoria per questo fine settimana", "Next week – {timeLocale}" : "Prossima settimana – {timeLocale}", "Set reminder for next week" : "Imposta promemoria per la prossima settimana", + "Could not apply tag, configured tag not found" : "Non è stato possibile applicare l’etichetta: l’etichetta configurata non è stata trovata", + "Could not move thread, destination mailbox not found" : "Non è stato possibile spostare la conversazione: la cartella di destinazione non è stata trovata", + "Could not execute quick action" : "Non è stato possibile eseguire l’azione rapida", "Quick action executed" : "Azione rapida eseguita", + "No trash folder configured" : "Nessuna cartella Cestino configurata", "Could not delete message" : "Impossibile eliminare il messaggio", "Could not archive message" : "Impossibile archiviare il messaggio", + "Thread was snoozed" : "Conversazione posticipata", + "Could not snooze thread" : "Non è possibile posticipare la conversazione", + "Thread was unsnoozed" : "Posicipo della conversazione annullato", + "Could not unsnooze thread" : "Non è stato possibile annullare il posticipo della conversazione", + "This summary was AI generated" : "Questo riepilogo è stato generato tramite IA", "Encrypted message" : "Messaggio cifrato", "This message is unread" : "Questo messaggio è non letto", "Unfavorite" : "Rimuovi preferito", @@ -273,6 +309,8 @@ "Mark not spam" : "Marca come posta non indesiderata", "Mark as spam" : "Marca come posta indesiderata", "Edit tags" : "Modifica etichette", + "Snooze" : "Posticipa", + "Unsnooze" : "Annulla posticipo", "Move thread" : "Sposta conversazione", "Move Message" : "Sposta messaggio", "Archive thread" : "Archivia la discussione", @@ -281,7 +319,9 @@ "Delete message" : "Elimina messaggio", "More actions" : "Altre azioni", "Back" : "Indietro", + "Set custom snooze" : "Imposta posticipo personalizzato", "Edit as new message" : "Modifica come nuovo messaggio", + "Reply with meeting" : "Rispondi proponendo una riunione", "Create task" : "Crea attività", "Download message" : "Scarica messaggio", "Back to all actions" : "Torna a tutte le azioni", @@ -294,16 +334,22 @@ "_Unfavorite {number}_::_Unfavorite {number}_" : ["Rimuovi dai preferiti {number}","Rimuovi dai preferiti {number}","Rimuovi dai preferiti {number}"], "_Favorite {number}_::_Favorite {number}_" : ["Aggiungi ai preferiti {number}","Aggiungi ai preferiti {number}","Aggiungi ai preferiti {number}"], "_Unselect {number}_::_Unselect {number}_" : ["Deseleziona {number}","Deseleziona {number}","Deseleziona {number}"], + "_Mark {number} as spam_::_Mark {number} as spam_" : ["Segna {number} come spam","Segna {number} come spam","Segna {number} come spam"], + "_Mark {number} as not spam_::_Mark {number} as not spam_" : ["Segna {number} come non spam","Segna {number} come non spam","Segna {number} come non spam"], + "_Edit tags for {number}_::_Edit tags for {number}_" : ["Modifica etichetta per {number}","Modifica etichette per {number}","Modifica etichette per {number}"], "_Move {number} thread_::_Move {number} threads_" : ["Sposta {number} conversazione","Sposta {number} conversazioni","Sposta {number} conversazioni"], "_Forward {number} as attachment_::_Forward {number} as attachment_" : ["Inoltra {number} come allegato","Inoltra {number} come allegati","Inoltra {number} come allegati"], "Mark as unread" : "Segna come non letto", "Mark as read" : "Segna come letto", + "Mark as unimportant" : "Segna come non importante", + "Mark as important" : "Segna come importante", "Report this bug" : "Segnala questo bug", "Event created" : "Evento creato", "Could not create event" : "Impossibile creare l'evento", "Create event" : "Crea evento", "All day" : "Tutto il giorno", "Attendees" : "Partecipanti", + "You can only invite attendees if your account has an email address set" : "Puoi invitare partecipanti solo se il tuo account ha un indirizzo email configurato", "Select calendar" : "Seleziona calendario", "Description" : "Descrizione", "Create" : "Crea", @@ -326,43 +372,79 @@ "Decline" : "Rifiuta", "Tentatively accept" : "Accetta provvisoriamente", "More options" : "Altre opzioni", + "This message has an attached invitation but the invitation dates are in the past" : "Questo messaggio contiene un invito, ma le date dell’invito sono già passate.", + "This message has an attached invitation but the invitation does not contain a participant that matches any configured mail account address" : "Questo messaggio contiene un invito, ma nell’invito non è presente alcun partecipante associato agli indirizzi email configurati.", + "Could not remove internal address {sender}" : "Non è stato possibile rimuovere l’indirizzo interno {sender}", + "Could not add internal address {address}" : "Non è stato possibile aggiungere l’indirizzo interno {address}", "individual" : "individuale", "domain" : "dominio", "Remove" : "Rimuovi", "Add internal address" : "Aggiungi indirizzo interno", + "Add internal email or domain" : "Aggiungi indirizzo email o dominio interno", "Itinerary for {type} is not supported yet" : "L'itinerario per {type} non è ancora supportato", + "To archive a message please configure an archive folder in account settings" : "Per archiviare un messaggio, configura prima una cartella Archivio nelle impostazioni dell’account", "You are not allowed to move this message to the archive folder and/or delete this message from the current folder" : "Non ti è consentito spostare questo messaggio nella cartella degli archivi e/o eliminare questo messaggio dalla cartella corrente.", + "Your IMAP server does not support storing the seen/unseen state." : "Il server IMAP non supporta il salvataggio dello stato letto/non letto.", + "Could not mark message as seen/unseen" : "Non è stato possibile segnare il messaggio come letto/non letto", "Last hour" : "Ultima ora", "Today" : "Oggi", "Yesterday" : "Ieri", "Last week" : "Ultima settimana", "Last month" : "Ultimo mese", + "Could not open folder" : "Non è possibile aprire la cartella", + "Loading messages …" : "Caricamento messaggi in corso …", + "Indexing your messages. This can take a bit longer for larger folders." : "Indicizzazione dei messaggi in corso. Per le cartelle più grandi l’operazione potrebbe richiedere più tempo.", "Choose target folder" : "Scegli la cartella di destinazione", "No more submailboxes in here" : "Qui non ci sono altre sotto-caselle di posta", + "Messages will automatically be marked as important using AI. The system learns from which messages you interact with or mark as important. In the beginning you might have to manually change the importance to teach it, but it will improve over time" : "I messaggi verranno contrassegnati automaticamente come importanti tramite l’IA. Il sistema impara dai messaggi con cui interagisci o che segni come importanti. All’inizio potresti dover modificare manualmente il livello di importanza per addestrarlo, ma la precisione migliorerà nel tempo.", + "Messages that you marked as favorite will be shown at the top of folders. You can disable this behavior in the app settings" : "I messaggi che hai aggiunto ai preferiti verranno mostrati in cima alle cartelle. Puoi disattivare questa opzione dalle impostazioni di Posta.", + "AI identifies messages sent by you that likely require a reply but did not receive one after a couple of days and shows them here" : "L’IA identifica i messaggi inviati da te che probabilmente richiedono una risposta ma che non ne hanno ricevuta dopo alcuni giorni, e li mostra qui.", "Favorites" : "Preferiti", + "Favorites info" : "Informazioni sui preferiti", + "Load more favorites" : "Carica altri preferiti", + "Follow up" : "Follow-up", + "Follow up info" : "Informazioni sui follow-up", + "Load more follow ups" : "Carica altri follow-up", "Important info" : "Informazione importante", + "Load more important messages" : "Carica altri messaggi importanti", "Other" : "Altro", + "Load more other messages" : "Carica altri messaggi", "Could not send mdn" : "Impossibile inviare mdn", "The sender of this message has asked to be notified when you read this message." : "Il mittente di questo messaggio ha chiesto di essere avvisato quando leggi questo messaggio.", "Notify the sender" : "Notifica al mittente", "You sent a read confirmation to the sender of this message." : "Hai inviato una conferma di lettura al mittente di questo messaggio.", + "Message was snoozed" : "Messaggio posticipato", + "Could not snooze message" : "Non è possibile posticipare il messaggio", + "Message was unsnoozed" : "Posticipo del messaggio annullato", + "Could not unsnooze message" : "Non è stato possibile annullare il posticipo del messaggio", "Forward" : "Inoltra", "Move message" : "Sposta messaggio", "Translate" : "Traduci", + "Forward message as attachment" : "Inoltra il messaggio come allegato", "View source" : "Visualizza sorgente", "Print message" : "Stampa messaggio", + "Create mail filter" : "Crea filtro email", "Download thread data for debugging" : "Scarica i dati del conversazione per il debug", + "Suggested replies are using AI" : "Le risposte suggerite sono generate tramite IA", "Message body" : "Corpo del messaggio", + "Warning: The S/MIME signature of this message is unverified. The sender might be impersonating someone!" : "Attenzione: la firma S/MIME di questo messaggio non è verificata. Il mittente potrebbe non essere chi dichiara di essere.", + "AI info" : "IA informazioni", "Unnamed" : "Senza nome", "Embedded message" : "Messaggio incorporato", + "Attachment saved to Files" : "Allegato salvato in Files", + "Attachment could not be saved" : "L'allegato non può essere salvato", "calendar imported" : "calendario importato", "Choose a folder to store the attachment in" : "Scegli una cartella dove salvare l'allegato", "Import into calendar" : "Importa nel calendario", "Download attachment" : "Scarica allegato", "Save to Files" : "Salva in File", + "Attachments saved to Files" : "Allegati salvati in Files", + "Error while saving attachments" : "Errore durante il salvataggio degli allegati", + "View fewer attachments" : "Mostra meno allegati", "Choose a folder to store the attachments in" : "Scegli una cartella in cui salvare gli allegati", "Save all to Files" : "Salva tutto in File", "Download Zip" : "Scarica Zip", + "_View {count} more attachment_::_View {count} more attachments_" : ["Mostra altri {count} allegati","Mostra altri {count} allegati","Mostra altri {count} allegati"], "This message is encrypted with PGP. Install Mailvelope to decrypt it." : "Questo messaggio è cifrato con PGP. Installa Mailvelope per decifrarlo.", "The images have been blocked to protect your privacy." : "Le immagini sono state bloccate per proteggere la tua riservatezza.", "Show images" : "Mostra le immagini", @@ -375,11 +457,18 @@ "Moving" : "Spostamento", "Moving thread" : "Spostamento conversazione", "Moving message" : "Spostamento messaggio", + "This account cannot connect" : "Questo account non riesce a connettersi", + "Connection failed. Please verify your information and try again" : "Connessione non riuscita. Verifica i dati inseriti e riprova.", "Used quota: {quota}% ({limit})" : "Spazio utilizzato: {quota} % ({limit})", "Used quota: {quota}%" : "Spazio utilizzato: {quota}%", + "Unable to create mailbox. The name likely contains invalid characters. Please try another name." : "Non è stato possibile creare la cartella. Il nome contiene probabilmente caratteri non validi. Prova a usare un altro nome.", "Remove account" : "Rimuovi account", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "L'account per {email} e i dati di posta elettronica memorizzati nella cache saranno rimossi da Nextcloud, ma non dal tuo fornitore di posta elettronica.", "Remove {email}" : "Rimuovi {email}", + "could not delete account" : "Non è stato possibile eliminare l’account", + "Provisioned account is disabled" : "L’account configurato automaticamente è disattivato", + "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "Accedi con password per abilitare questo account. La sessione corrente usa un accesso senza password, ad esempio SSO o WebAuthn.", + "Delegate account" : "Account delegato", "Show only subscribed folders" : "Mostra solo le cartelle sottoscritte", "Add folder" : "Aggiungi cartella", "Folder name" : "Nome della cartella", @@ -392,19 +481,26 @@ "_{total} message_::_{total} messages_" : ["{total} messaggio","{total} messaggi","{total} messaggi"], "_{unread} unread of {total}_::_{unread} unread of {total}_" : ["{unread} non letto di {total}","{unread} non letti di {total}","{unread} non letti di {total}"], "Loading …" : "Caricamento in corso...", + "All messages in mailbox will be deleted." : "Tutti i messaggi presenti nella cartella verranno eliminati.", + "Clear mailbox {name}" : "Svuota cartella {name}", + "Clear folder" : "Svuota cartella", "The folder and all messages in it will be deleted." : "La cartella e tutti i messaggi in essa saranno eliminati.", "Delete folder" : "Elimina cartella", "Delete folder {name}" : "Elimina cartella {name}", "An error occurred, unable to rename the mailbox." : "Si è verificato un errore, impossibile rinominare la casella di posta.", + "Please wait 10 minutes before repairing again" : "Attendi 10 minuti prima di ripetere la riparazione", "Mark all as read" : "Marca tutti come letti", "Add subfolder" : "Aggiungi sottocartella", "Rename" : "Rinomina", "Move folder" : "Sposta cartella", + "Repair folder" : "Ripara cartella", "Clear cache" : "Svuota cache", "Clear locally cached data, in case there are issues with synchronization." : "Cancella i dati locali in cache, nel caso ci siano problemi di sincronizzazione.", "Subscribed" : "Sottoscritta", "Sync in background" : "Sincronizza in background", + "Delete all messages" : "Elimina tutti i messaggi", "Outbox" : "Posta in uscita", + "Translate this message to {language}" : "Traduci questo messaggio in {language}", "New message" : "Nuovo messaggio", "Edit message" : "Modifica messaggio", "Draft" : "Bozza", @@ -414,36 +510,59 @@ "Failed to save draft" : "Impossibile salvare la bozza", "attachment" : "allegato", "attached" : "allegato", + "No \"sent\" folder configured. Please pick one in the account settings." : "Nessuna cartella “Posta inviata” configurata. Selezionane una nelle impostazioni dell’account.", "You are trying to send to many recipients in To and/or Cc. Consider using Bcc to hide recipient addresses." : "Stai tentando di inviare a troppi destinatari in A e/o CC. Considera l'idea di utilizzare CCN per nascondere gli indirizzi dei destinatari.", "Your message has no subject. Do you want to send it anyway?" : "Il tuo messaggio non ha oggetto. Vuoi inviarlo comunque?", "You mentioned an attachment. Did you forget to add it?" : "Hai menzionato un allegato. Hai dimenticato di aggiungerlo?", "Message discarded" : "Messaggio scartato", "Could not discard message" : "Impossibile eliminare il messaggio", + "Maximize composer" : "Espandi finestra", + "Show recipient details" : "Mostra dettagli dei destinatari", + "Hide recipient details" : "Nascondi dettagli dei destinatari", + "Minimize composer" : "Riduci finestra", "Error sending your message" : "Errore di invio del messaggio", "Retry" : "Riprova", "Warning sending your message" : "Avviso durante l'invio del tuo messaggio", "Send anyway" : "Invia comunque", - "Autoresponder off" : "Risponditore automatico spento", - "Autoresponder on" : "Risponditore automatico attivo", + "Welcome to {productName} Mail" : "Benvenuto in {productName} Mail", + "Start writing a message by clicking below or select an existing message to display its contents" : "Inizia a scrivere un messaggio cliccando qui sotto oppure seleziona un messaggio esistente per visualizzarne il contenuto", + "Autoresponder off" : "Risposta automatica disattivata", + "Autoresponder on" : "Risposta automatica attiva", + "Autoresponder follows system settings" : "La risposta automatica segue le impostazioni di sistema", + "The autoresponder follows your personal absence period settings." : "La risposta automatica segue le impostazioni del tuo periodo di assenza.", + "Edit absence settings" : "Modifica impostazioni di assenza", "First day" : "Primo giorno", "Last day (optional)" : "Ultimo giorno (facoltativo)", + "${subject} will be replaced with the subject of the message you are responding to" : "${subject} verrà sostituito con l’oggetto del messaggio a cui stai rispondendo", "Message" : "Messaggio", "Oh Snap!" : "Accidenti!", - "Save autoresponder" : "Salva risponditore automatico", + "Save autoresponder" : "Salva risposta automatica", "Could not open outbox" : "Impossibile aprire la posta in uscita", + "Pending or not sent messages will show up here" : "Qui verranno mostrati i messaggi in sospeso o non ancora inviati", + "Could not copy to \"Sent\" folder" : "Non è possibile copiare nella cartella \"Posta inviata\"", + "Mail server error" : "Errore del server di posta", "Message could not be sent" : "Il messaggio non può essere inviato", "Message deleted" : "Messaggio eliminato", + "Copy to \"Sent\" Folder" : "Copia nella cartella \"Posta inviata\"", + "Copy to Sent Folder" : "Copia nella cartella Posta inviata", + "Phishing email" : "Email di phishing", + "This email might be a phishing attempt" : "Questa email potrebbe essere un tentativo di phishing", + "Hide suspicious links" : "Nascondi link sospetti", + "Show suspicious links" : "Mostra link sospetti", + "link text" : "testo del link", "Copied email address to clipboard" : "Indirizzo email copiato negli appunti", "Could not copy email address to clipboard" : "Impossibile copiare l'indirizzo email negli appunti", "Contacts with this address" : "Contatti con questo indirizzo", "Add to Contact" : "Aggiungi ai contatti", "New Contact" : "Nuovo contatto", "Copy to clipboard" : "Copia negli appunti", + "Contact name …" : "Nome contatto …", "Add" : "Aggiungi", "Show less" : "Mostra meno", "Show more" : "Mostra più", "Clear" : "Pulisci", "Search in folder" : "Cerca nella cartella", + "Open search modal" : "Apri finestra di ricerca", "Close" : "Chiudi", "Search parameters" : "Parametri di ricerca", "Search subject" : "Cerca nell'oggetto", @@ -458,9 +577,13 @@ "Select BCC recipients" : "Seleziona i destinatari CCN", "Tags" : "Etichette", "Select tags" : "Seleziona etichette", + "Marked as" : "Contrassegnato come", "Has attachments" : "Ha allegati", + "Mentions me" : "Mi menziona", + "Has attachment" : "Con allegato", + "To me" : "A me", "Enable mail body search" : "Abilita ricerca nel corpo del messaggio", - "Sieve is a powerful language for writing filters for your mailbox. You can manage the sieve scripts in Mail if your email service supports it. Sieve is also required to use Autoresponder and Filters." : "Sieve è un potente linguaggio per la creazione di filtri per la tua casella di posta. Puoi gestire gli script Sieve nell’app Posta se il tuo servizio email lo supporta. Sieve è inoltre necessario per utilizzare il risponditore automatico e i filtri.", + "Sieve is a powerful language for writing filters for your mailbox. You can manage the sieve scripts in Mail if your email service supports it. Sieve is also required to use Autoresponder and Filters." : "Sieve è un potente linguaggio per la creazione di filtri per la tua casella di posta. Puoi gestire gli script Sieve nell’app Posta se il tuo servizio email lo supporta. Sieve è inoltre necessario per utilizzare le risposte automatiche e i filtri.", "Enable sieve filter" : "Abilita filtri Sieve", "Sieve host" : "Host Sieve", "Sieve security" : "Sicurezza Sieve", @@ -470,33 +593,67 @@ "Custom" : "Personalizzato", "Sieve User" : "Utente Sieve", "Sieve Password" : "Password Sieve", + "Oh snap!" : "Ops!", "Save sieve settings" : "Salva impostazione Sieve", + "The syntax seems to be incorrect:" : "La sintassi non sembra corretta:", "Save sieve script" : "Salva script Sieve", "Signature …" : "Scrivi firma …", + "Your signature is larger than 2 MB. This may affect the performance of your editor." : "La firma supera i 2 MB e potrebbe rallentare l’editor.", "Save signature" : "Salva firma", "Place signature above quoted text" : "Posiziona la firma sopra il testo citato", "Message source" : "Sorgente del messaggio", "An error occurred, unable to rename the tag." : "Si è verificato un errore, impossibile rinominare l'etichetta.", "Edit name or color" : "Modifica nome o colore", + "Saving new tag name …" : "Salvataggio del nuovo nome dell’etichetta in corso …", "Delete tag" : "Elimina etichetta", + "Set tag" : "Assegna etichetta", + "Unset tag" : "Rimuovi etichetta", + "Tag name is a hidden system tag" : "Questo nome è riservato a un’etichetta di sistema nascosta", "Tag already exists" : "L'etichetta esiste già", + "Tag name cannot be empty" : "Il nome dell’etichetta non può essere vuoto", "An error occurred, unable to create the tag." : "Si è verificato un errore, impossibile creare l'etichetta.", "Add default tags" : "Aggiungi etichette predefiniti", "Add tag" : "Aggiungi etichetta", + "Saving tag …" : "Salvataggio etichetta in corso …", "Task created" : "Attività creata", "Could not create task" : "Impossibile creare l'attività", + "No calendars with task list support" : "Nessun calendario supporta le liste di attività", + "Summarizing thread failed." : "Non è stato possibile generare il riepilogo della conversazione.", "Could not load your message thread" : "Impossibile caricare la tua discussione", + "The thread doesn't exist or has been deleted" : "La conversazione non esiste oppure è stata eliminata", + "Email was not able to be opened" : "Non è stato possibile aprire l’email", "Print" : "Stampa", "Could not print message" : "Impossibile stampare il messaggio", + "Loading thread" : "Caricamento conversazione in corso", "Not found" : "Non trovato", "Encrypted & verified " : "Cifrato e verificato", "Signature verified" : "Firma verificata", "Signature unverified " : "Firma non verificata", "This message was encrypted by the sender before it was sent." : "Questo messaggio è stato cifrato dal mittente prima di essere inviato.", + "This message contains a verified digital S/MIME signature. The message wasn't changed since it was sent." : "Questo messaggio contiene una firma digitale S/MIME verificata. Il messaggio non è stato modificato dopo l’invio.", + "This message contains an unverified digital S/MIME signature. The message might have been changed since it was sent or the certificate of the signer is untrusted." : "Questo messaggio contiene una firma digitale S/MIME non verificata. Il messaggio potrebbe essere stato modificato dopo l’invio oppure il certificato del firmatario potrebbe non essere attendibile.", "Reply all" : "Rispondi a tutti", + "Unsubscribe request sent" : "Richiesta di disiscrizione inviata", + "Could not unsubscribe from mailing list" : "Non è stato possibile disiscriversi dalla mailing list", + "Please wait for the message to load" : "Attendi il caricamento del messaggio", + "Disable reminder" : "Disattiva promemoria", "Unsubscribe" : "Rimuovi sottoscrizione", "Reply to sender only" : "Rispondi solo al mittente", + "Mark as unfavorite" : "Rimuovi dai preferiti", + "Mark as favorite" : "Aggiungi ai preferiti", + "To:" : "A: ", + "Cc:" : "Cc:", + "Bcc:" : "Bcc:", + "Unsubscribe via link" : "Disiscriviti tramite link", + "Unsubscribing will stop all messages from the mailing list {sender}" : "Disiscrivendoti non riceverai più messaggi da questa mailing list {sender}", + "Send unsubscribe email" : "Invia email di disiscrizione", + "Unsubscribe via email" : "Disiscriviti tramite email", + "{name} Assistant" : "{name} Assistente", + "Thread summary" : "Riepilogo della conversazione", "Go to latest message" : "Vai all'ultimo messaggio", + "Newest message" : "Messaggio più recente", + "This summary is AI generated and may contain mistakes." : "Questo riepilogo è stato generato tramite IA e potrebbe contenere imprecisioni.", + "Please select languages to translate to and from" : "Seleziona la lingua di partenza e quella di destinazione per la traduzione", "The message could not be translated" : "Questo messaggio non può essere tradotto", "Translation copied to clipboard" : "Traduzione copiata negli appunti", "Translation could not be copied" : "La traduzione non può essere copiata", @@ -506,6 +663,7 @@ "Target language to translate into" : "Lingua di destinazione in cui tradurre", "Translate to" : "Traduci in", "Translating" : "Traduco", + "This translation is generated using AI and may contain inaccuracies" : "Questa traduzione è stata generata con l’IA e potrebbe contenere imprecisioni", "Copy translated text" : "Copia testo tradotto", "Disable trash retention by leaving the field empty or setting it to 0. Only mails deleted after enabling trash retention will be processed." : "Disattiva la conservazione nel cestino lasciando il campo vuoto o impostandolo a 0. Verranno elaborati solo i messaggi eliminati dopo l’attivazione della conservazione.", "Could not remove trusted sender {sender}" : "Impossibile rimuovere il mittente fidato {sender}", @@ -520,15 +678,48 @@ "{trainNr} from {depStation} to {arrStation}" : "{trainNr} da {depStation} a {arrStation}", "Train from {depStation} to {arrStation}" : "Treno da {depStation} a {arrStation}", "Train" : "Treno", + "Mark message as" : "Contrassegna il messaggio come", + "Add flag" : "Aggiungi contrassegno", "Move into folder" : "Sposta nella cartella", "Stop" : "Ferma", + "Delete action" : "Elimina azione", + "Answered" : "Risposto", "Deleted" : "Eliminato", + "Flagged" : "Contrassegnato", + "Seen" : "Letto", + "Enter flag" : "Inserisci contrassegno", + "Stop ends all processing" : "Interrompi: termina tutte le elaborazioni", + "Sender" : "Mittente", "Recipient" : "Destinatario", + "Create a new mail filter" : "Crea un nuovo filtro email", + "Choose the headers you want to use to create your filter. In the next step, you will be able to refine the filter conditions and specify the actions to be taken on messages that match your criteria." : "Scegli le intestazioni da usare per creare il filtro. Nel passaggio successivo potrai definire meglio le condizioni e indicare quali azioni applicare ai messaggi che corrispondono ai criteri scelti.", "Delete filter" : "Elimina filtro", + "Delete mail filter {filterName}?" : "Cancella filtro email {filterName}?", + "Are you sure to delete the mail filter?" : "Sei sicuro di cancellare il filtro email?", + "New filter" : "Nuovo filtro", + "Filter saved" : "Filtro salvato", + "Could not save filter" : "Impossibile salvare il filtro", + "Filter deleted" : "Filtro cancellato", + "Could not delete filter" : "Impossibile cancellare il filtro", + "Take control of your email chaos. Filters help you to prioritize what matters and eliminate clutter." : "Metti ordine nella tua posta. I filtri ti aiutano a dare priorità ai messaggi importanti e a ridurre il superfluo.", + "Hang tight while the filters load" : "Caricamento dei filtri in corso", + "Filter is active" : "Il filtro è attivo", + "Filter is not active" : "Il filtro non è attivo", + "If all the conditions are met, the actions will be performed" : "Se tutte le condizioni sono soddisfatte, verranno eseguite le azioni previste", + "If any of the conditions are met, the actions will be performed" : "Se almeno una delle condizioni è soddisfatta, verranno eseguite le azioni previste", "Help" : "Assistenza", "contains" : "contiene", + "A substring match. The field matches if the provided value is contained within it. For example, \"report\" would match \"port\"." : "Corrispondenza parziale. Il campo corrisponde se contiene il valore indicato. Ad esempio, “report” può corrispondere a “Business report 2024”.", "matches" : "corrisponde", + "A pattern match using wildcards. The \"*\" symbol represents any number of characters (including none), while \"?\" represents exactly one character. For example, \"*report*\" would match \"Business report 2024\"." : "Corrispondenza tramite caratteri jolly. Il simbolo “*” rappresenta qualsiasi numero di caratteri (anche nessuno), mentre “?” rappresenta esattamente un carattere. Ad esempio, “*report*” può corrispondere a “Business report 2024”.", + "Enter subject" : "Inserisci oggetto", + "Enter sender" : "Inserisci mittente", + "Enter recipient" : "Inserisci destinatario", + "is exactly" : "è esattamente", + "Conditions" : "Condizioni", + "Add condition" : "Aggiungi condizione", "Actions" : "Azioni", + "Add action" : "Aggiungi azione", "Priority" : "Priorità", "Enable filter" : "Abilita filtri", "Tag" : "Etichetta", @@ -536,11 +727,19 @@ "Edit quick action" : "Modifica azione rapida", "Add quick action" : "Aggiungi azione rapida", "Quick action deleted" : "Azione rapida eliminata", + "Failed to delete quick action" : "Non è stato possibile eliminare l’azione rapida", + "Failed to update quick action" : "Non è stato possibile aggiornare l’azione rapida", + "Failed to update step in quick action" : "Non è stato possibile aggiornare il passaggio dell’azione rapida", "Quick action updated" : "Azione rapida aggiornata", + "Failed to create quick action" : "Non è stato possibile creare l’azione rapida", + "Failed to add steps to quick action" : "Non è stato possibile aggiungere passaggi all’azione rapida", "Quick action created" : "Azione rapida creata", + "Failed to delete action step" : "Non è stato possibile eliminare il passaggio dell’azione", "No quick actions yet." : "Nessuna azione rapida disponibile al momento.", "Edit" : "Modifica", "Quick action name" : "Azione rapida nome", + "Do the following actions" : "Esegui le seguenti azioni", + "Add another action" : "Aggiungi un’altra azione", "Successfully updated config for \"{domain}\"" : "Configurazione aggiornata con successo per \"{domain}\"", "Error saving config" : "Errore durante il salvataggio della configurazione", "Saved config for \"{domain}\"" : "Configurazione salvata per \"{domain}\"", @@ -549,6 +748,7 @@ "There was an error when provisioning accounts." : "Si è verificato un errore durante il approvvigionamento degli account.", "Successfully deleted and deprovisioned accounts for \"{domain}\"" : " Account eliminati e approvvigionato correttamente per \"{domain}\"", "Error when deleting and deprovisioning accounts for \"{domain}\"" : "Errore durante l'eliminazione e il approvvigionamento degli account per \"{domain}\"", + "Could not save default classification setting" : "Non è stato possibile salvare l’impostazione di classificazione predefinita", "Mail app" : "Applicazione di posta", "The mail app allows users to read mails on their IMAP accounts." : "L'applicazione di posta consente agli utenti di leggere i messaggi dei propri account IMAP.", "Here you can find instance-wide settings. User specific settings are found in the app itself (bottom-left corner)." : "Qui puoi trovare le impostazioni a livello di istanza. Le impostazioni specifiche dell'utente si trovano nell'applicazione stessa (angolo in basso a sinistra).", @@ -564,11 +764,28 @@ "Provisioning Configurations" : "Configurazione di approvvigionamento", "Add new config" : "Aggiungi nuova configurazione", "Provision all accounts" : "Predisponi tutti gli account", + "Allow additional mail accounts" : "Consenti account email aggiuntivi", + "Allow additional Mail accounts from User Settings" : "Consenti account Mail aggiuntivi dalle impostazioni utente", + "Enable text processing through LLMs" : "Abilita elaborazione del testo tramite LLM", + "The Mail app can process user data with the help of the configured large language model and provide assistance features like thread summaries, smart replies and event agendas." : "L’app Mail può elaborare i dati dell’utente tramite il modello linguistico configurato e offrire funzionalità avanzate come riepiloghi delle conversazioni, risposte intelligenti e agenda degli eventi.", + "Enable LLM processing" : "Abilita elaborazione tramite LLM", + "Enable classification by importance by default" : "Abilita di default la classificazione per importanza", + "The Mail app can classify incoming emails by importance using machine learning. This feature is enabled by default but can be disabled by default here. Individual users will still be able to toggle the feature for their accounts." : "L’app Mail può classificare automaticamente le email in arrivo in base alla loro importanza tramite machine learning. La funzionalità è attiva per impostazione predefinita, ma può essere disattivata qui. I singoli utenti potranno comunque abilitarla o disabilitarla per il proprio account.", + "Enable classification of important mails by default" : "Abilita di default la classificazione delle email importanti", "Anti Spam Service" : "Servizio anti-spam", "You can set up an anti spam service email address here." : "È possibile impostare un indirizzo e-mail del servizio Anti-Spam qui.", "Any email that is marked as spam will be sent to the anti spam service." : "Qualsiasi email contrassegnata come posta indesiderata verrà inviata al servizio anti-spam.", "Gmail integration" : "Integrazione Gmail", + "Gmail allows users to access their email via IMAP. For security reasons this access is only possible with an OAuth 2.0 connection or Google accounts that use two-factor authentication and app passwords." : "Gmail consente l’accesso alle email tramite IMAP. Per motivi di sicurezza, questa modalità è disponibile solo tramite una connessione OAuth 2.0 oppure per gli account Google con autenticazione a due fattori e password per le app.", + "You have to register a new Client ID for a \"Web application\" in the Google Cloud console. Add the URL {url} as authorized redirect URI." : "Devi registrare un nuovo Client ID di tipo “Applicazione web” nella console Google Cloud. Aggiungi l’URL {url} come URI di reindirizzamento autorizzato.", "Microsoft integration" : "Integrazione Microsoft", + "Microsoft requires you to access your emails via IMAP using OAuth 2.0 authentication. To do this, you need to register an app with Microsoft Entra ID, formerly known as Microsoft Azure Active Directory." : "Microsoft richiede l’accesso alle email tramite IMAP con autenticazione OAuth 2.0. Per configurarlo, devi registrare un’app in Microsoft Entra ID, in precedenza noto come Microsoft Azure Active Directory.", + "Redirect URI" : "URI di reindirizzamento", + "For more details, please click here to open our documentation." : "Per maggiori dettagli, fai clic qui per aprire la documentazione.", + "User Interface Preference Defaults" : "Preferenze predefinite dell’interfaccia utente", + "These settings are used to pre-configure the user interface preferences they can be overridden by the user in the mail settings" : "Queste impostazioni servono a preconfigurare le preferenze dell’interfaccia utente. L’utente potrà comunque modificarle dalle impostazioni di Posta.", + "Message View Mode" : "Modalità di visualizzazione dei messaggi", + "Show only the selected message" : "Mostra solo il messaggio selezionato", "Successfully set up anti spam email addresses" : "Impostare correttamente gli indirizzi e-mail anti-spam", "Error saving anti spam email addresses" : "Errore durante il salvataggio degli indirizzi email anti-spam", "Successfully deleted anti spam reporting email" : "Email di segnalazione anti-spam eliminata con successo", @@ -621,6 +838,7 @@ "The provided PKCS #12 certificate must contain at least one certificate and exactly one private key." : "Il certificato PKCS #12 fornito deve contenere almeno un certificato ed esattamente una chiave privata.", "Failed to import the certificate. Please check the password." : "Impossibile importare il certificato. Controlla la password.", "Certificate imported successfully" : "Certificato importato correttamente", + "Failed to import the certificate. Please make sure that the private key matches the certificate and is not protected by a passphrase." : "Non è stato possibile importare il certificato. Verifica che la chiave privata corrisponda al certificato e che non sia protetta da passphrase.", "Failed to import the certificate" : "Importare del certificato non riuscita", "S/MIME certificates" : "Certificati S/MIME", "Certificate name" : "Nome del certificato", @@ -637,13 +855,24 @@ "The private key is only required if you intend to send signed and encrypted emails using this certificate." : "La chiave privata è necessaria solo se si intende inviare email firmate e cifrate utilizzando questo certificato.", "Submit" : "Invia", "No text blocks available" : "Nessun blocco di testo disponibile", + "Text block deleted" : "Blocco di testo cancellato", + "Failed to delete text block" : "Cancellazione testo di blocco fallita", + "Text block shared with {sharee}" : "Blocco di testo condiviso con {sharee}", + "Failed to share text block with {sharee}" : "Non è stato possibile condividere il blocco di testo con {sharee}", + "Share deleted for {name}" : "Condivisione rimossa per {name}", + "Failed to delete share with {name}" : "Non è stato possibile rimuovere la condivisione con {name}", "Guest" : "Ospite", "Group" : "Gruppo", + "Failed to save text block" : "Salvataggio testo di blocco fallito", "Shared" : "Condiviso", "Edit {title}" : "Modifica {title}", "Delete {title}" : "Elimina {title}", + "Edit text block" : "Modifica blocco di testo", "Shares" : "Condivisioni", + "Search for users or groups" : "Cerca utenti o gruppi", + "Choose a text block to insert at the cursor" : "Scegli un blocco di testo da inserire nel punto selezionato", "Insert" : "Inserisci", + "Insert text block" : "Inserisci testo di blocco", "Account connected" : "Account connesso", "You can close this window" : "Puoi chiudere questa finestra", "Connect your mail account" : "Connetti il tuo account di posta", @@ -665,15 +894,22 @@ "Discard changes" : "Scarta le modifiche", "Discard unsaved changes" : "Scartare le modifiche non salvate", "Keep editing message" : "Continua a modificare il messaggio", + "(All or part of this reply was generated by AI)" : "(Questa risposta è stata generata interamente o parzialmente dall'IA)", "Attachments were not copied. Please add them manually." : "Gli allegati non sono stati copiati. Aggiungili manualmente.", + "Could not create snooze mailbox" : "Non è stato possibile creare la cartella per i messaggi posticipati", + "Sorry, the message could not be loaded. The draft may no longer exist. Please refresh the page and try again." : "Non è stato possibile caricare il messaggio. La bozza potrebbe non esistere più. Aggiorna la pagina e riprova.", + "Sending message…" : "Invio del messaggio in corso...", "Message sent" : "Messaggio inviato", "Could not send message" : "Impossibile inviare il messaggio", + "Message copied to \"Sent\" folder" : "Messaggio copiato nella cartella “Posta inviata”", + "Could not copy message to \"Sent\" folder" : "Non è possibile copiare messaggi nella cartella \"Posta inviata\"", "Could not load {tag}{name}{endtag}" : "Impossibile caricare {tag}{name}{endtag}", "There was a problem loading {tag}{name}{endtag}" : "Si è verificato un programma durante il caricamento di {tag}{name}{endtag}", "Could not load your message" : "Impossibile caricare il tuo messaggio", "Could not load the desired message" : "Impossibile caricare messaggio desiderato", "Could not load the message" : "Impossibile caricare il messaggio", "Error loading message" : "Errore durante il caricamento del messaggio", - "Messages will automatically be marked as important based on which messages you interacted with or marked as important. In the beginning you might have to manually change the importance to teach the system, but it will improve over time." : "I messaggi saranno automaticamente contrassegnati come importanti in base ai messaggi con cui hai interagito o contrassegnati come importanti. All'inizio potresti dover cambiare manualmente l'importanza per addestrare il sistema, ma migliorerà nel tempo." + "Messages will automatically be marked as important based on which messages you interacted with or marked as important. In the beginning you might have to manually change the importance to teach the system, but it will improve over time." : "I messaggi saranno automaticamente contrassegnati come importanti in base ai messaggi con cui hai interagito o contrassegnati come importanti. All'inizio potresti dover cambiare manualmente l'importanza per addestrare il sistema, ma migliorerà nel tempo.", + "Messages sent by you that require a reply but did not receive one after a couple of days will be shown here." : "Qui verranno mostrati i messaggi che hai inviato e che richiedono una risposta, ma che non ne hanno ricevuta una dopo alcuni giorni." },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" } \ No newline at end of file diff --git a/l10n/zh_TW.js b/l10n/zh_TW.js index 471ed796f5..e0495d6924 100644 --- a/l10n/zh_TW.js +++ b/l10n/zh_TW.js @@ -137,6 +137,7 @@ OC.L10N.register( "Mail settings" : "電子郵件設定", "General" : "一般", "Set as default mail app" : "設定為預設的郵件應用程式", + "{email} (delegated)" : "{email}(已委派)", "Add mail account" : "新增郵件帳號", "Appearance" : "外觀", "Show all messages in thread" : "在討論串中顯示所有訊息", @@ -255,7 +256,21 @@ OC.L10N.register( "Expand composer" : "展開編輯器", "Close composer" : "關閉編輯器", "Confirm" : "確認", + "Delegate access" : "委派存取權", "Revoke" : "撤銷", + "{userId} will no longer be able to act on your behalf" : "{userId} 將無法再代表您執行操作", + "Could not fetch delegates" : "無法擷取委派使用者", + "Delegated access to {userId}" : "已向 {userId} 委派存取權", + "Could not delegate access" : "無法委派存取權", + "Revoked access for {userId}" : "已撤銷對 {userId} 的存取", + "Could not revoke delegation" : "無法撤銷委派", + "Delegation" : "委派", + "Allow users to send, receive, and delete mail on your behalf" : "允許使用者代表您傳送、接收與刪除郵件", + "Revoke access" : "撤銷存取權", + "Add delegate" : "新增委派", + "Select a user" : "選取使用者", + "They will be able to send, receive, and delete mail on your behalf" : "他們將能代表您傳送、接收與刪除郵件", + "Revoke access?" : "撤銷存取權", "Tag: {name} deleted" : "標籤:{name} 已刪除", "An error occurred, unable to delete the tag." : "遇到錯誤,無法刪除標籤。", "The tag will be deleted from all messages." : "該標籤將從所有訊息刪除。", @@ -452,8 +467,10 @@ OC.L10N.register( "Remove account" : "移除帳號", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "{email} 帳號與已快取的電子郵件將會從 Nextcloud 移除,但不會從您的電子郵件提供商移除。", "Remove {email}" : "移除 {email}", + "could not delete account" : "無法刪除帳號", "Provisioned account is disabled" : "預先設定的帳號已停用", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "請使用密碼登入以啟用此帳號。目前的工作階段正在使用無密碼身份驗證,例如 SSO 或 WebAuthn。", + "Delegate account" : "委派帳號", "Show only subscribed folders" : "僅顯示已訂閱的資料夾", "Add folder" : "新增資料夾", "Folder name" : "資料夾名稱", diff --git a/l10n/zh_TW.json b/l10n/zh_TW.json index 2510b5cf9b..985eaa449d 100644 --- a/l10n/zh_TW.json +++ b/l10n/zh_TW.json @@ -135,6 +135,7 @@ "Mail settings" : "電子郵件設定", "General" : "一般", "Set as default mail app" : "設定為預設的郵件應用程式", + "{email} (delegated)" : "{email}(已委派)", "Add mail account" : "新增郵件帳號", "Appearance" : "外觀", "Show all messages in thread" : "在討論串中顯示所有訊息", @@ -253,7 +254,21 @@ "Expand composer" : "展開編輯器", "Close composer" : "關閉編輯器", "Confirm" : "確認", + "Delegate access" : "委派存取權", "Revoke" : "撤銷", + "{userId} will no longer be able to act on your behalf" : "{userId} 將無法再代表您執行操作", + "Could not fetch delegates" : "無法擷取委派使用者", + "Delegated access to {userId}" : "已向 {userId} 委派存取權", + "Could not delegate access" : "無法委派存取權", + "Revoked access for {userId}" : "已撤銷對 {userId} 的存取", + "Could not revoke delegation" : "無法撤銷委派", + "Delegation" : "委派", + "Allow users to send, receive, and delete mail on your behalf" : "允許使用者代表您傳送、接收與刪除郵件", + "Revoke access" : "撤銷存取權", + "Add delegate" : "新增委派", + "Select a user" : "選取使用者", + "They will be able to send, receive, and delete mail on your behalf" : "他們將能代表您傳送、接收與刪除郵件", + "Revoke access?" : "撤銷存取權", "Tag: {name} deleted" : "標籤:{name} 已刪除", "An error occurred, unable to delete the tag." : "遇到錯誤,無法刪除標籤。", "The tag will be deleted from all messages." : "該標籤將從所有訊息刪除。", @@ -450,8 +465,10 @@ "Remove account" : "移除帳號", "The account for {email} and cached email data will be removed from Nextcloud, but not from your email provider." : "{email} 帳號與已快取的電子郵件將會從 Nextcloud 移除,但不會從您的電子郵件提供商移除。", "Remove {email}" : "移除 {email}", + "could not delete account" : "無法刪除帳號", "Provisioned account is disabled" : "預先設定的帳號已停用", "Please login using a password to enable this account. The current session is using passwordless authentication, e.g. SSO or WebAuthn." : "請使用密碼登入以啟用此帳號。目前的工作階段正在使用無密碼身份驗證,例如 SSO 或 WebAuthn。", + "Delegate account" : "委派帳號", "Show only subscribed folders" : "僅顯示已訂閱的資料夾", "Add folder" : "新增資料夾", "Folder name" : "資料夾名稱", From cdd65ce03f389efe2542914efc73acbb4001edb5 Mon Sep 17 00:00:00 2001 From: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> Date: Tue, 19 May 2026 09:57:36 +0200 Subject: [PATCH 039/228] chore(agents): switch AI attribution trailer to Assisted-by format Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> --- AGENTS.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b91db95b1a..3283688d64 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -124,8 +124,8 @@ The branch will be rebased and squashed into a clean history before merge (CI en ### Commit Message Format -All commits must include two lines at the end: -1. Agent/model attribution: `AI-assisted: (model)` +All commits must include two trailers at the end: +1. Agent/model attribution: `Assisted-by: :` 2. DCO sign-off: Use `git commit -s` to add automatically When committing, use: `git commit -m "message" -s` @@ -139,7 +139,7 @@ fix(deps): Update package dependencies - Updated package X to latest stable version - Verified all tests pass -AI-assisted: OpenCode (Claude Haiku 4.5) +Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Name ``` From 3cb80369336a34dbd12940bc60ad0fa977e0fddb Mon Sep 17 00:00:00 2001 From: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> Date: Tue, 19 May 2026 09:57:46 +0200 Subject: [PATCH 040/228] chore(agents): use lowercase present-tense verbs in commit example Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> --- AGENTS.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3283688d64..2826b4299d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -134,10 +134,10 @@ This ensures the sign-off includes your configured Git user email. Example: ``` -fix(deps): Update package dependencies +fix(deps): update package dependencies -- Updated package X to latest stable version -- Verified all tests pass +- update package X to latest stable version +- verify all tests pass Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Name From 2e1ddaec2d45636f69a70c338fe5caa7b659ffd2 Mon Sep 17 00:00:00 2001 From: Hamza Date: Wed, 8 Apr 2026 14:28:29 +0200 Subject: [PATCH 041/228] feat: add notifications for account delegation Signed-off-by: Hamza --- lib/Controller/DelegationController.php | 4 +- lib/Notification/Notifier.php | 54 +++++++++- lib/Service/DelegationService.php | 51 ++++++++- .../Controller/DelegationControllerTest.php | 6 +- tests/Unit/Service/DelegationServiceTest.php | 101 ++++++++++++++++-- 5 files changed, 198 insertions(+), 18 deletions(-) diff --git a/lib/Controller/DelegationController.php b/lib/Controller/DelegationController.php index 487e094ef9..5e5502372e 100644 --- a/lib/Controller/DelegationController.php +++ b/lib/Controller/DelegationController.php @@ -86,7 +86,7 @@ public function delegate(int $accountId, string $userId): JSONResponse { } try { - $delegation = $this->delegationService->delegate($accountId, $userId); + $delegation = $this->delegationService->delegate($account, $userId, $this->currentUserId); } catch (DelegationExistsException) { return new JSONResponse(['message' => 'Delegation already exists'], Http::STATUS_CONFLICT); } @@ -111,7 +111,7 @@ public function unDelegate(int $accountId, string $userId): JSONResponse { return new JSONResponse([], Http::STATUS_UNAUTHORIZED); } - $this->delegationService->unDelegate($accountId, $userId); + $this->delegationService->unDelegate($account, $userId, $this->currentUserId); return new JSONResponse([], Http::STATUS_OK); } } diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php index dedd2e7ebd..f3402b3cee 100644 --- a/lib/Notification/Notifier.php +++ b/lib/Notification/Notifier.php @@ -72,8 +72,60 @@ public function prepare(INotification $notification, string $languageCode): INot ] ]); break; + case 'account_delegation': + $notification->setIcon($this->url->getAbsoluteURL( + $this->url->linkTo('mail', 'img/delegation.svg') + )); + $parameters = $notification->getSubjectParameters(); + $messageParameters = $notification->getMessageParameters(); + $delegated = $messageParameters['delegated']; + if ($delegated) { + $notification->setRichSubject($l->t('{account_email} has been delegated to you'), [ + 'account_email' => [ + 'type' => 'highlight', + 'id' => (string)$parameters['id'], + 'name' => $parameters['account_email'] + ] + ]); + $notification->setRichMessage($l->t('{user} delegated {account} to you'), + [ + 'user' => [ + 'type' => 'user', + 'id' => $messageParameters['current_user_id'], + 'name' => $messageParameters['current_user_display_name'], + ], + 'account' => [ + 'type' => 'highlight', + 'id' => (string)$messageParameters['id'], + 'name' => $messageParameters['account_email'] + ] + ]); + } else { + $notification->setRichSubject($l->t('{account_email} is no longer delegated to you'), [ + 'account_email' => [ + 'type' => 'highlight', + 'id' => (string)$parameters['id'], + 'name' => $parameters['account_email'] + ] + ]); + $notification->setRichMessage($l->t('{user} revoked delagation for {account}'), + [ + 'user' => [ + 'type' => 'user', + 'id' => $messageParameters['current_user_id'], + 'name' => $messageParameters['current_user_display_name'], + ], + 'account' => [ + 'type' => 'highlight', + 'id' => (string)$messageParameters['id'], + 'name' => $messageParameters['account_email'] + ] + ]); + } + + break; default: - throw new UnknownNotificationException(); + throw new UnknownNotificationException(); } return $notification; diff --git a/lib/Service/DelegationService.php b/lib/Service/DelegationService.php index d94e9fdbf8..79b335b7cf 100644 --- a/lib/Service/DelegationService.php +++ b/lib/Service/DelegationService.php @@ -9,6 +9,7 @@ namespace OCA\Mail\Service; +use OCA\Mail\Account; use OCA\Mail\Db\AliasMapper; use OCA\Mail\Db\Delegation; use OCA\Mail\Db\DelegationMapper; @@ -18,6 +19,9 @@ use OCA\Mail\Exception\ClientException; use OCA\Mail\Exception\DelegationExistsException; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IUserManager; +use OCP\Notification\IManager; class DelegationService { @@ -28,10 +32,14 @@ public function __construct( private MessageMapper $messageMapper, private AliasMapper $aliasMapper, private LocalMessageMapper $localMessageMapper, + private IUserManager $userManager, + private IManager $notificationManager, + private ITimeFactory $time, ) { } - public function delegate(int $accountId, string $userId): Delegation { + public function delegate(Account $account, string $userId, string $currentUserId): Delegation { + $accountId = $account->getId(); try { $this->delegationMapper->find($accountId, $userId); throw new DelegationExistsException("Delegation already exists for account $accountId and user $userId"); @@ -42,17 +50,21 @@ public function delegate(int $accountId, string $userId): Delegation { $delegation = new Delegation(); $delegation->setAccountId($accountId); $delegation->setUserId($userId); - return $this->delegationMapper->insert($delegation); + $result = $this->delegationMapper->insert($delegation); + $this->notify($userId, $currentUserId, $account, true); + return $result; } public function findDelegatedToUsersForAccount(int $accountId): array { return $this->delegationMapper->findDelegatedToUsers($accountId); } - public function unDelegate(int $accountId, string $userId): void { + public function unDelegate(Account $account, string $userId, string $currentUserId): void { try { + $accountId = $account->getId(); $delegation = $this->delegationMapper->find($accountId, $userId); $this->delegationMapper->delete($delegation); + $this->notify($userId, $currentUserId, $account, false); } catch (DoesNotExistException $e) { // shouldn't end up here // delegation not found nothing to undelegate @@ -112,4 +124,37 @@ public function resolveLocalMessageUserId(int $localMessageId, string $currentUs $accountId = $this->localMessageMapper->findAccountIdForLocalMessage($localMessageId); return $this->resolveAccountUserId($accountId, $currentUserId); } + + /** + * Send a notification on delegation + * @param string $userId The user the account is being delegated to + * @param string $currentUserId Current user + * @param Account $account The delegated account + * @param bool $delegated true for delegate|false for undelegate + * @return void + */ + private function notify(string $userId, string $currentUserId, Account $account, bool $delegated) { + $notification = $this->notificationManager->createNotification(); + $displayName = $this->userManager->getExistingUser($currentUserId)->getDisplayName(); + $time = $this->time->getDateTime('now'); + $notification + ->setApp('mail') + ->setUser($userId) + ->setObject('delegation', (string)$account->getId()) + ->setSubject('account_delegation', [ + 'id' => $account->getId(), + 'account_email' => $account->getEmail(), + + ]) + ->setDateTime($time) + ->setMessage('account_delegation_changed', [ + 'id' => $account->getId(), + 'delegated' => $delegated, + 'current_user_id' => $currentUserId, + 'current_user_display_name' => $displayName, + 'account_email' => $account->getEmail(), + ] + ); + $this->notificationManager->notify($notification); + } } diff --git a/tests/Unit/Controller/DelegationControllerTest.php b/tests/Unit/Controller/DelegationControllerTest.php index e0a6798c71..582ff6ce91 100644 --- a/tests/Unit/Controller/DelegationControllerTest.php +++ b/tests/Unit/Controller/DelegationControllerTest.php @@ -122,7 +122,7 @@ public function testDelegateSuccess(): void { $this->delegationService->expects($this->once()) ->method('delegate') - ->with(1, 'delegatee') + ->with($this->ownAccount, 'delegatee', $this->currentUserId) ->willReturn($delegation); $response = $this->controller->delegate(1, 'delegatee'); @@ -217,7 +217,7 @@ public function testDelegateAlreadyExists(): void { $this->delegationService->expects($this->once()) ->method('delegate') - ->with(1, 'delegatee') + ->with($this->ownAccount, 'delegatee', $this->currentUserId) ->willThrowException(new DelegationExistsException('Delegation already exists')); $response = $this->controller->delegate(1, 'delegatee'); @@ -235,7 +235,7 @@ public function testUnDelegateSuccess(): void { $this->delegationService->expects($this->once()) ->method('unDelegate') - ->with(1, 'delegatee'); + ->with($this->ownAccount, 'delegatee', $this->currentUserId); $response = $this->controller->unDelegate(1, 'delegatee'); diff --git a/tests/Unit/Service/DelegationServiceTest.php b/tests/Unit/Service/DelegationServiceTest.php index c0ab17becf..70ad916c41 100644 --- a/tests/Unit/Service/DelegationServiceTest.php +++ b/tests/Unit/Service/DelegationServiceTest.php @@ -24,7 +24,6 @@ use OCA\Mail\Service\DelegationService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; -use OCP\EventDispatcher\IEventDispatcher; use OCP\IUser; use OCP\IUserManager; use OCP\Notification\IManager; @@ -41,7 +40,6 @@ class DelegationServiceTest extends TestCase { private IUserManager&MockObject $userManager; private IManager&MockObject $notificationManager; private ITimeFactory&MockObject $timeFactory; - private IEventDispatcher&MockObject $eventDispatcher; private DelegationService $service; private Account $account; @@ -58,7 +56,6 @@ protected function setUp(): void { $this->userManager = $this->createMock(IUserManager::class); $this->notificationManager = $this->createMock(IManager::class); $this->timeFactory = $this->createMock(ITimeFactory::class); - $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->service = new DelegationService( $this->delegationMapper, @@ -67,6 +64,9 @@ protected function setUp(): void { $this->messageMapper, $this->aliasMapper, $this->localMessageMapper, + $this->userManager, + $this->notificationManager, + $this->timeFactory, ); $mailAccount = new MailAccount(); @@ -76,7 +76,7 @@ protected function setUp(): void { $this->account = new Account($mailAccount); } - private function mockNotification(): void { + private function mockNotification(): INotification&MockObject { $notification = $this->createMock(INotification::class); $notification->method('setApp')->willReturnSelf(); $notification->method('setUser')->willReturnSelf(); @@ -89,8 +89,10 @@ private function mockNotification(): void { $user = $this->createMock(IUser::class); $user->method('getDisplayName')->willReturn('Owner User'); - $this->userManager->method('get')->with('owner')->willReturn($user); + $this->userManager->method('getExistingUser')->with('owner')->willReturn($user); $this->timeFactory->method('getDateTime')->willReturn(new \DateTime()); + + return $notification; } public function testDelegateSuccess(): void { @@ -112,7 +114,7 @@ public function testDelegateSuccess(): void { return $d; }); - $result = $this->service->delegate($this->account->getId(), 'delegatee'); + $result = $this->service->delegate($this->account, 'delegatee', 'owner'); $this->assertEquals(1, $result->getAccountId()); $this->assertEquals('delegatee', $result->getUserId()); @@ -133,7 +135,7 @@ public function testDelegateThrowsWhenAlreadyExists(): void { $this->expectException(DelegationExistsException::class); - $this->service->delegate($this->account->getId(), 'delegatee'); + $this->service->delegate($this->account, 'delegatee', 'owner'); } public function testFindDelegatedToUsersForAccount(): void { @@ -169,10 +171,12 @@ public function testUnDelegateSuccess(): void { ->method('delete') ->with($delegation); - $this->service->unDelegate($this->account->getId(), 'delegatee'); + $this->service->unDelegate($this->account, 'delegatee', 'owner'); } public function testUnDelegateWhenNotFound(): void { + $this->mockNotification(); + $this->delegationMapper->expects($this->once()) ->method('find') ->with(1, 'delegatee') @@ -181,7 +185,7 @@ public function testUnDelegateWhenNotFound(): void { $this->delegationMapper->expects($this->never()) ->method('delete'); - $this->service->unDelegate($this->account->getId(), 'delegatee'); + $this->service->unDelegate($this->account, 'delegatee', 'owner'); } public function testResolveAccountUserIdOwner(): void { @@ -310,4 +314,83 @@ public function testResolveLocalMessageUserId(): void { $this->assertEquals('owner', $result); } + + public function testDelegateSendsNotification(): void { + $notification = $this->mockNotification(); + $now = new \DateTime(); + $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->timeFactory->method('getDateTime')->willReturn($now); + + $this->delegationMapper->method('find') + ->willThrowException(new DoesNotExistException('Not found')); + $this->delegationMapper->method('insert') + ->willReturnCallback(function (Delegation $d) { + $d->setId(10); + return $d; + }); + + $notification->expects($this->once())->method('setApp')->with('mail')->willReturnSelf(); + $notification->expects($this->once())->method('setUser')->with('delegatee')->willReturnSelf(); + $notification->expects($this->once())->method('setObject')->with('delegation', '1')->willReturnSelf(); + $notification->expects($this->once()) + ->method('setSubject') + ->with('account_delegation', [ + 'id' => 1, + 'account_email' => 'owner@example.com', + ]) + ->willReturnSelf(); + $notification->expects($this->once())->method('setDateTime')->willReturnSelf(); + $notification->expects($this->once()) + ->method('setMessage') + ->with('account_delegation_changed', [ + 'id' => 1, + 'delegated' => true, + 'current_user_id' => 'owner', + 'current_user_display_name' => 'Owner User', + 'account_email' => 'owner@example.com', + ]) + ->willReturnSelf(); + $this->notificationManager->expects($this->once()) + ->method('notify') + ->with($notification); + + $this->service->delegate($this->account, 'delegatee', 'owner'); + } + + public function testUnDelegateSendsRevokedNotification(): void { + $notification = $this->mockNotification(); + + $delegation = new Delegation(); + $delegation->setId(10); + $delegation->setAccountId(1); + $delegation->setUserId('delegatee'); + + $this->delegationMapper->method('find')->willReturn($delegation); + + $notification->expects($this->once())->method('setApp')->with('mail')->willReturnSelf(); + $notification->expects($this->once())->method('setUser')->with('delegatee')->willReturnSelf(); + $notification->expects($this->once())->method('setObject')->with('delegation', '1')->willReturnSelf(); + $notification->expects($this->once()) + ->method('setSubject') + ->with('account_delegation', [ + 'id' => 1, + 'account_email' => 'owner@example.com', + ]) + ->willReturnSelf(); + $notification->expects($this->once()) + ->method('setMessage') + ->with('account_delegation_changed', [ + 'id' => 1, + 'delegated' => false, + 'current_user_id' => 'owner', + 'current_user_display_name' => 'Owner User', + 'account_email' => 'owner@example.com', + ]) + ->willReturnSelf(); + $this->notificationManager->expects($this->once()) + ->method('notify') + ->with($notification); + + $this->service->unDelegate($this->account, 'delegatee', 'owner'); + } } From dc0799b51fd8545e3069ed9dc344565e2f5ca208 Mon Sep 17 00:00:00 2001 From: Hamza Date: Thu, 14 May 2026 12:14:59 +0200 Subject: [PATCH 042/228] test: cover delegation branches in Notifier AI-assisted: Claude Opus 4.7 (1M context) Signed-off-by: Hamza --- lib/Notification/Notifier.php | 2 +- lib/Service/DelegationService.php | 12 +- tests/Unit/Notification/NotifierTest.php | 185 +++++++++++++++++++ tests/Unit/Service/DelegationServiceTest.php | 6 +- 4 files changed, 201 insertions(+), 4 deletions(-) create mode 100644 tests/Unit/Notification/NotifierTest.php diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php index f3402b3cee..a89684a382 100644 --- a/lib/Notification/Notifier.php +++ b/lib/Notification/Notifier.php @@ -108,7 +108,7 @@ public function prepare(INotification $notification, string $languageCode): INot 'name' => $parameters['account_email'] ] ]); - $notification->setRichMessage($l->t('{user} revoked delagation for {account}'), + $notification->setRichMessage($l->t('{user} revoked delegation for {account}'), [ 'user' => [ 'type' => 'user', diff --git a/lib/Service/DelegationService.php b/lib/Service/DelegationService.php index 79b335b7cf..588ca42731 100644 --- a/lib/Service/DelegationService.php +++ b/lib/Service/DelegationService.php @@ -22,6 +22,8 @@ use OCP\AppFramework\Utility\ITimeFactory; use OCP\IUserManager; use OCP\Notification\IManager; +use OCP\Notification\IncompleteNotificationException; +use Psr\Log\LoggerInterface; class DelegationService { @@ -35,6 +37,7 @@ public function __construct( private IUserManager $userManager, private IManager $notificationManager, private ITimeFactory $time, + private LoggerInterface $logger, ) { } @@ -135,7 +138,7 @@ public function resolveLocalMessageUserId(int $localMessageId, string $currentUs */ private function notify(string $userId, string $currentUserId, Account $account, bool $delegated) { $notification = $this->notificationManager->createNotification(); - $displayName = $this->userManager->getExistingUser($currentUserId)->getDisplayName(); + $displayName = $this->userManager->get($currentUserId)?->getDisplayName() ?? $currentUserId; $time = $this->time->getDateTime('now'); $notification ->setApp('mail') @@ -155,6 +158,11 @@ private function notify(string $userId, string $currentUserId, Account $account, 'account_email' => $account->getEmail(), ] ); - $this->notificationManager->notify($notification); + + try { + $this->notificationManager->notify($notification); + } catch (IncompleteNotificationException $e) { + $this->logger->warning('Failed to send delegation notification to {userId}', ['userId' => $userId,'exception' => $e]); + } } } diff --git a/tests/Unit/Notification/NotifierTest.php b/tests/Unit/Notification/NotifierTest.php new file mode 100644 index 0000000000..9ac2fb716d --- /dev/null +++ b/tests/Unit/Notification/NotifierTest.php @@ -0,0 +1,185 @@ +factory = $this->createMock(IFactory::class); + $this->url = $this->createMock(IURLGenerator::class); + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->method('t')->willReturnArgument(0); + $this->factory->method('get')->willReturn($this->l10n); + + $this->notifier = new Notifier($this->factory, $this->url); + } + + public function testGetID(): void { + $this->assertEquals('mail', $this->notifier->getID()); + } + + public function testGetName(): void { + $this->assertEquals('Mail', $this->notifier->getName()); + } + + public function testPrepareForeignAppThrows(): void { + $notification = $this->createMock(INotification::class); + $notification->method('getApp')->willReturn('other'); + + $this->expectException(UnknownNotificationException::class); + + $this->notifier->prepare($notification, 'en'); + } + + public function testPrepareUnknownSubjectThrows(): void { + $notification = $this->createMock(INotification::class); + $notification->method('getApp')->willReturn('mail'); + $notification->method('getSubject')->willReturn('something_unknown'); + + $this->expectException(UnknownNotificationException::class); + + $this->notifier->prepare($notification, 'en'); + } + + public function testPrepareAccountDelegationDelegated(): void { + $notification = $this->createMock(INotification::class); + $notification->method('getApp')->willReturn('mail'); + $notification->method('getSubject')->willReturn('account_delegation'); + $notification->method('getSubjectParameters')->willReturn([ + 'id' => 1, + 'account_email' => 'owner@example.com', + ]); + $notification->method('getMessageParameters')->willReturn([ + 'id' => 1, + 'delegated' => true, + 'current_user_id' => 'owner', + 'current_user_display_name' => 'Owner User', + 'account_email' => 'owner@example.com', + ]); + + $this->url->method('linkTo')->with('mail', 'img/delegation.svg')->willReturn('/apps/mail/img/delegation.svg'); + $this->url->method('getAbsoluteURL')->with('/apps/mail/img/delegation.svg')->willReturn('https://example.com/apps/mail/img/delegation.svg'); + + $notification->expects($this->once()) + ->method('setIcon') + ->with('https://example.com/apps/mail/img/delegation.svg') + ->willReturnSelf(); + $notification->expects($this->once()) + ->method('setRichSubject') + ->with( + '{account_email} has been delegated to you', + [ + 'account_email' => [ + 'type' => 'highlight', + 'id' => '1', + 'name' => 'owner@example.com', + ], + ] + ) + ->willReturnSelf(); + $notification->expects($this->once()) + ->method('setRichMessage') + ->with( + '{user} delegated {account} to you', + [ + 'user' => [ + 'type' => 'user', + 'id' => 'owner', + 'name' => 'Owner User', + ], + 'account' => [ + 'type' => 'highlight', + 'id' => '1', + 'name' => 'owner@example.com', + ], + ] + ) + ->willReturnSelf(); + + $result = $this->notifier->prepare($notification, 'en'); + + $this->assertSame($notification, $result); + } + + public function testPrepareAccountDelegationRevoked(): void { + $notification = $this->createMock(INotification::class); + $notification->method('getApp')->willReturn('mail'); + $notification->method('getSubject')->willReturn('account_delegation'); + $notification->method('getSubjectParameters')->willReturn([ + 'id' => 1, + 'account_email' => 'owner@example.com', + ]); + $notification->method('getMessageParameters')->willReturn([ + 'id' => 1, + 'delegated' => false, + 'current_user_id' => 'owner', + 'current_user_display_name' => 'Owner User', + 'account_email' => 'owner@example.com', + ]); + + $this->url->method('linkTo')->with('mail', 'img/delegation.svg')->willReturn('/apps/mail/img/delegation.svg'); + $this->url->method('getAbsoluteURL')->with('/apps/mail/img/delegation.svg')->willReturn('https://example.com/apps/mail/img/delegation.svg'); + + $notification->expects($this->once()) + ->method('setIcon') + ->with('https://example.com/apps/mail/img/delegation.svg') + ->willReturnSelf(); + $notification->expects($this->once()) + ->method('setRichSubject') + ->with( + '{account_email} is no longer delegated to you', + [ + 'account_email' => [ + 'type' => 'highlight', + 'id' => '1', + 'name' => 'owner@example.com', + ], + ] + ) + ->willReturnSelf(); + $notification->expects($this->once()) + ->method('setRichMessage') + ->with( + '{user} revoked delegation for {account}', + [ + 'user' => [ + 'type' => 'user', + 'id' => 'owner', + 'name' => 'Owner User', + ], + 'account' => [ + 'type' => 'highlight', + 'id' => '1', + 'name' => 'owner@example.com', + ], + ] + ) + ->willReturnSelf(); + + $result = $this->notifier->prepare($notification, 'en'); + + $this->assertSame($notification, $result); + } +} diff --git a/tests/Unit/Service/DelegationServiceTest.php b/tests/Unit/Service/DelegationServiceTest.php index 70ad916c41..14b84275e3 100644 --- a/tests/Unit/Service/DelegationServiceTest.php +++ b/tests/Unit/Service/DelegationServiceTest.php @@ -29,6 +29,7 @@ use OCP\Notification\IManager; use OCP\Notification\INotification; use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; class DelegationServiceTest extends TestCase { private DelegationMapper&MockObject $delegationMapper; @@ -40,6 +41,7 @@ class DelegationServiceTest extends TestCase { private IUserManager&MockObject $userManager; private IManager&MockObject $notificationManager; private ITimeFactory&MockObject $timeFactory; + private LoggerInterface&MockObject $logger; private DelegationService $service; private Account $account; @@ -56,6 +58,7 @@ protected function setUp(): void { $this->userManager = $this->createMock(IUserManager::class); $this->notificationManager = $this->createMock(IManager::class); $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->logger = $this->createMock(LoggerInterface::class); $this->service = new DelegationService( $this->delegationMapper, @@ -67,6 +70,7 @@ protected function setUp(): void { $this->userManager, $this->notificationManager, $this->timeFactory, + $this->logger, ); $mailAccount = new MailAccount(); @@ -89,7 +93,7 @@ private function mockNotification(): INotification&MockObject { $user = $this->createMock(IUser::class); $user->method('getDisplayName')->willReturn('Owner User'); - $this->userManager->method('getExistingUser')->with('owner')->willReturn($user); + $this->userManager->method('get')->with('owner')->willReturn($user); $this->timeFactory->method('getDateTime')->willReturn(new \DateTime()); return $notification; From 98f6282008460663bd312d66d4398ae01aa7c188 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Thu, 15 Feb 2024 21:12:21 +0100 Subject: [PATCH 043/228] feat(ui): paste or copy several e-mail addresses into address fields at once Signed-off-by: Andrii Rublov --- src/components/Composer.vue | 99 +++++++---- src/tests/unit/util/emailAddress.spec.js | 213 +++++++++++++++++++++++ src/util/emailAddress.js | 132 ++++++++++++++ 3 files changed, 414 insertions(+), 30 deletions(-) create mode 100644 src/tests/unit/util/emailAddress.spec.js create mode 100644 src/util/emailAddress.js diff --git a/src/components/Composer.vue b/src/components/Composer.vue index 6bbf7cc0fe..e8c217aeb1 100644 --- a/src/components/Composer.vue +++ b/src/components/Composer.vue @@ -509,7 +509,6 @@ import { showError, showWarning } from '@nextcloud/dialogs' import { getCanonicalLocale, getFirstDay, getLocale, translate as t } from '@nextcloud/l10n' import moment from '@nextcloud/moment' import { NcActionButton as ActionButton, NcActionCheckbox as ActionCheckbox, NcActionInput as ActionInput, NcActionRadio as ActionRadio, NcActions as Actions, NcButton as ButtonVue, NcListItemIcon as ListItemIcon, NcIconSvgWrapper, NcSelect } from '@nextcloud/vue' -import addressParser from 'address-rfc2822' import debouncePromise from 'debounce-promise' import debounce from 'lodash/fp/debounce.js' import trimStart from 'lodash/fp/trimCharsStart.js' @@ -544,6 +543,7 @@ import { findRecipient } from '../service/AutocompleteService.js' import { savePreference } from '../service/PreferenceService.js' import { EDITOR_MODE_HTML, EDITOR_MODE_TEXT } from '../store/constants.js' import useMainStore from '../store/mainStore.js' +import { parseEmailList } from '../util/emailAddress.js' import { formatDateTime } from '../util/formatDateTime.js' import { detect, html, toHtml, toPlain } from '../util/text.js' import textBlockSvg from './../../img/text_snippet.svg' @@ -1130,8 +1130,9 @@ export default { /** * Called once a user leaves the recipient picker. * - * If the user is typing something that looks like a valid email address, we clear the input (return true) - * because the related code in onNewAddr will add the value as a recipient. + * If the user is typing something that looks like a valid email address (or a + * pasted list of addresses), we clear the input (return true) because the + * related code in onNewAddr will add the value as a recipient. * * Otherwise, the user is still typing and we don't clear the input. * @@ -1140,7 +1141,7 @@ export default { */ clearOnBlur(event) { if (this.recipientSearchTerms[event]) { - return this.seemsValidEmailAddress(this.recipientSearchTerms[event]) + return parseEmailList(this.recipientSearchTerms[event]).length > 0 } return false }, @@ -1470,27 +1471,71 @@ export default { }, onNewAddr(option, list, type) { + // Build a Set of already-selected emails for O(1) case-insensitive dedup. + const existing = new Set(list.map((r) => r.email.toLowerCase())) + if ( (option === null || option === undefined) && this.recipientSearchTerms[type] !== undefined && this.recipientSearchTerms[type] !== '' ) { - if (!this.seemsValidEmailAddress(this.recipientSearchTerms[type])) { + const parsedFromSearch = parseEmailList(this.recipientSearchTerms[type]) + if (parsedFromSearch.length === 0) { return } - option = {} - option.email = this.recipientSearchTerms[type] - option.label = this.recipientSearchTerms[type] this.recipientSearchTerms[type] = '' + + let changed = false + for (const addr of parsedFromSearch) { + if (!existing.has(addr.email.toLowerCase())) { + const recipient = { ...addr } + this.newRecipients.push(recipient) + list.push(recipient) + existing.add(addr.email.toLowerCase()) + changed = true + } + } + if (changed) { + this.saveDraftDebounced() + } + return } - if (list.some((recipient) => recipient.email === option?.email) || !option) { + if (!option) { return } - const recipient = { ...option } - this.newRecipients.push(recipient) - list.push(recipient) - this.saveDraftDebounced() + + let changed = false + if (option.id) { + if (!existing.has(option.email.toLowerCase())) { + const recipient = { ...option } + this.newRecipients.push(recipient) + list.push(recipient) + changed = true + } + } else { + const emailList = parseEmailList(option.email) + // When createRecipientOption normalised a named address like + // "Jane Doe " it stored the display name in + // option.label but reduced option.email to the bare address. + // Re-parsing that bare address loses the display name, so for + // single-address options we trust option.label directly. + const recipientsToAdd = emailList.length === 1 + ? [{ email: option.email, label: option.label ?? option.email }] + : emailList + for (const addr of recipientsToAdd) { + if (!existing.has(addr.email.toLowerCase())) { + const recipient = { ...addr } + this.newRecipients.push(recipient) + list.push(recipient) + existing.add(addr.email.toLowerCase()) + changed = true + } + } + } + if (changed) { + this.saveDraftDebounced() + } }, async onSend() { @@ -1659,10 +1704,18 @@ export default { * @return {{email: string, label: string}} The new option */ createRecipientOption(value) { - if (!this.seemsValidEmailAddress(value)) { - throw new Error('Skipping because it does not look like a valid email address') + const parsed = parseEmailList(value) + if (parsed.length === 0) { + throw new Error('Skipping because it does not look like valid email address(es)') + } + // For a single address, return the normalised email/label so the chip + // shows a clean address rather than the raw typed/pasted string. + if (parsed.length === 1) { + return { email: parsed[0].email, label: parsed[0].label } } - return { email: value, label: value } + // For multi-address pastes keep the raw value so onNewAddr can expand + // all addresses from option.email. + return { email: value.trim(), label: value.trim() } }, /** @@ -1686,20 +1739,6 @@ export default { return option.email }, - /** - * True when value looks like a valid email address - * - * @param {string} value to check if email address - * @return {boolean} - */ - seemsValidEmailAddress(value) { - try { - addressParser.parse(value) - return true - } catch (error) { - return false - } - }, }, } diff --git a/src/tests/unit/util/emailAddress.spec.js b/src/tests/unit/util/emailAddress.spec.js new file mode 100644 index 0000000000..92a442db27 --- /dev/null +++ b/src/tests/unit/util/emailAddress.spec.js @@ -0,0 +1,213 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { getLabelAndAddress, parseEmailList } from '../../../util/emailAddress.js' + +describe('getLabelAndAddress', () => { + it('parses a plain email address', () => { + expect(getLabelAndAddress('alice@example.com')).toEqual({ + label: 'alice@example.com', + email: 'alice@example.com', + }) + }) + + it('parses an email with angle brackets', () => { + expect(getLabelAndAddress('')).toEqual({ + label: 'alice@example.com', + email: 'alice@example.com', + }) + }) + + it('parses a display name with angle bracket email', () => { + expect(getLabelAndAddress('Alice Smith ')).toEqual({ + label: 'Alice Smith', + email: 'alice@example.com', + }) + }) + + it('preserves uppercase email addresses', () => { + expect(getLabelAndAddress('User@Example.COM')).toEqual({ + label: 'User@Example.COM', + email: 'User@Example.COM', + }) + }) + + it('handles mixed case with display name', () => { + expect(getLabelAndAddress('Alice ')).toEqual({ + label: 'Alice', + email: 'Alice@Example.COM', + }) + }) + + it('returns null for invalid input', () => { + expect(getLabelAndAddress('not an email')).toBeNull() + }) + + it('returns null for empty string', () => { + expect(getLabelAndAddress('')).toBeNull() + }) + + it('returns null for null or undefined', () => { + expect(getLabelAndAddress(null)).toBeNull() + expect(getLabelAndAddress(undefined)).toBeNull() + }) + + it('handles trailing delimiter with trailing whitespace', () => { + expect(getLabelAndAddress('alice@example.com, ')).toEqual({ + label: 'alice@example.com', + email: 'alice@example.com', + }) + }) + + it('does not include trailing delimiters in the email', () => { + expect(getLabelAndAddress('alice@example.com,')).toEqual({ + label: 'alice@example.com', + email: 'alice@example.com', + }) + }) + + it('does not include trailing semicolons in the email', () => { + expect(getLabelAndAddress('alice@example.com;')).toEqual({ + label: 'alice@example.com', + email: 'alice@example.com', + }) + }) + + it('handles email with subdomains', () => { + expect(getLabelAndAddress('user@mail.example.co.uk')).toEqual({ + label: 'user@mail.example.co.uk', + email: 'user@mail.example.co.uk', + }) + }) + + it('handles email with special characters in local part', () => { + expect(getLabelAndAddress('user+tag@example.com')).toEqual({ + label: 'user+tag@example.com', + email: 'user+tag@example.com', + }) + }) +}) + +describe('parseEmailList', () => { + it('parses a single email', () => { + expect(parseEmailList('alice@example.com')).toEqual([ + { label: 'alice@example.com', email: 'alice@example.com' }, + ]) + }) + + it('parses comma-separated emails', () => { + expect(parseEmailList('alice@example.com, bob@example.com')).toEqual([ + { label: 'alice@example.com', email: 'alice@example.com' }, + { label: 'bob@example.com', email: 'bob@example.com' }, + ]) + }) + + it('parses semicolon-separated emails', () => { + expect(parseEmailList('alice@example.com; bob@example.com')).toEqual([ + { label: 'alice@example.com', email: 'alice@example.com' }, + { label: 'bob@example.com', email: 'bob@example.com' }, + ]) + }) + + it('parses space-separated emails', () => { + expect(parseEmailList('alice@example.com bob@example.com')).toEqual([ + { label: 'alice@example.com', email: 'alice@example.com' }, + { label: 'bob@example.com', email: 'bob@example.com' }, + ]) + }) + + it('parses emails with display names', () => { + expect(parseEmailList('Alice , Bob ')).toEqual([ + { label: 'Alice', email: 'alice@example.com' }, + { label: 'Bob', email: 'bob@example.com' }, + ]) + }) + + it('preserves uppercase email addresses', () => { + expect(parseEmailList('User@Example.COM, Another@TEST.org')).toEqual([ + { label: 'User@Example.COM', email: 'User@Example.COM' }, + { label: 'Another@TEST.org', email: 'Another@TEST.org' }, + ]) + }) + + it('handles mixed delimiters', () => { + expect(parseEmailList('a@example.com, b@example.com; c@example.com')).toEqual([ + { label: 'a@example.com', email: 'a@example.com' }, + { label: 'b@example.com', email: 'b@example.com' }, + { label: 'c@example.com', email: 'c@example.com' }, + ]) + }) + + it('ignores non-email entries in a list', () => { + expect(parseEmailList('not-an-email, alice@example.com')).toEqual([ + { label: 'alice@example.com', email: 'alice@example.com' }, + ]) + }) + + it('skips entries without any email address', () => { + expect(parseEmailList('just-text')).toEqual([]) + }) + + it('returns empty array for empty string', () => { + expect(parseEmailList('')).toEqual([]) + }) + + it('returns empty array for string without emails', () => { + expect(parseEmailList('just some text without emails')).toEqual([]) + }) + + it('handles trailing delimiters', () => { + expect(parseEmailList('alice@example.com, bob@example.com,')).toEqual([ + { label: 'alice@example.com', email: 'alice@example.com' }, + { label: 'bob@example.com', email: 'bob@example.com' }, + ]) + }) + + it('handles multiple addresses with angle brackets and display names', () => { + expect(parseEmailList('Alice Smith ; Bob Jones ')).toEqual([ + { label: 'Alice Smith', email: 'alice@example.com' }, + { label: 'Bob Jones', email: 'bob@example.com' }, + ]) + }) + + it('handles quoted display names containing commas', () => { + // address-rfc2822 normalizes "Last, First" to "First Last" per RFC 2822 + expect(parseEmailList('"Smith, Alice" , "Jones, Bob" ')).toEqual([ + { label: 'Alice Smith', email: 'alice@example.com' }, + { label: 'Bob Jones', email: 'bob@example.com' }, + ]) + }) + + it('extracts valid addresses from mixed input with invalid tokens', () => { + expect(parseEmailList('invalid-entry, "Smith, Alice" , not-an-email, bob@example.com')).toEqual([ + { label: 'Alice Smith', email: 'alice@example.com' }, + { label: 'bob@example.com', email: 'bob@example.com' }, + ]) + }) + + it('returns empty array for null or undefined', () => { + expect(parseEmailList(null)).toEqual([]) + expect(parseEmailList(undefined)).toEqual([]) + }) + + // Regression tests from PR description / issue #6013 + it('handles real-world paste: plain addresses with display name (issue #6013 case 1)', () => { + const result = parseEmailList('test@test.com, Jane Doe, MSc jane@doe.tld') + expect(result).toEqual([ + { label: 'test@test.com', email: 'test@test.com' }, + { label: 'jane@doe.tld', email: 'jane@doe.tld' }, + ]) + }) + + it('handles real-world paste: messy mixed input (issue #6013 case 2)', () => { + const input = 'ian eiloart iane@example.ac.uk>;shuf6@example.ac.uk,, test+user@company.c, "ian,eiloart", <@example.com:foo@example.ac.uk>, foo@#,ian@-example.com, ian@one@two;asdas< test@test.com> test@test.com, Newasd Na@,me >; testaaaa@aasd.com' + const result = parseEmailList(input) + const emails = result.map((r) => r.email) + expect(emails).toContain('shuf6@example.ac.uk') + expect(emails).toContain('ian@example.ac.uk') + expect(emails).toContain('test@test.com') + expect(emails).toContain('testaaaa@aasd.com') + }) +}) diff --git a/src/util/emailAddress.js b/src/util/emailAddress.js new file mode 100644 index 0000000000..8a6ed6e0b1 --- /dev/null +++ b/src/util/emailAddress.js @@ -0,0 +1,132 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import addressParser from 'address-rfc2822' + +/** + * Try to parse a string with address-rfc2822. + * Returns an array of {label, email} objects, or an empty array on failure. + * + * @param {string} str The input string + * @return {Array<{ label: string, email: string }>} + */ +function tryParse(str) { + if (!str) { + return [] + } + + try { + return addressParser.parse(str).map((addr) => ({ + label: addr.name() || addr.address, + email: addr.address, + })) + } catch { + return [] + } +} + +/** + * Split a string on delimiter characters (commas and semicolons) that are + * not inside quotes or angle brackets. + * + * @param {string} str The input string + * @return {string[]} The parts + */ +function splitOnDelimiters(str) { + const parts = [] + let current = '' + let inQuotes = false + let inAngle = false + + for (let i = 0; i < str.length; i++) { + const ch = str[i] + + if (ch === '"' && (i === 0 || str[i - 1] !== '\\')) { + inQuotes = !inQuotes + } else if (!inQuotes && ch === '<') { + inAngle = true + } else if (!inQuotes && ch === '>') { + inAngle = false + } + + if ((ch === ',' || ch === ';') && !inQuotes && !inAngle) { + parts.push(current) + current = '' + } else { + current += ch + } + } + parts.push(current) + return parts +} + +/** + * Extract a label and email address from a string like "John Doe " + * or just "john@example.com". + * + * @param {string|null|undefined} str The input string + * @return {{ label: string, email: string } | null} Parsed result or null if no email found + */ +export function getLabelAndAddress(str) { + if (!str) { + return null + } + + // Trim first so trailing delimiters followed by whitespace are still removed + const cleaned = str.trim().replace(/[,;]+$/, '').trim() + const results = tryParse(cleaned) + return results.length > 0 ? results[0] : null +} + +/** + * Parse a string containing one or more email addresses separated by + * commas or semicolons, with limited support for spaces between bare + * email addresses. + * + * Supports formats like: + * - "alice@example.com, bob@example.com" + * - "Alice ; Bob " + * - "alice@example.com bob@example.com" (bare email addresses only) + * + * @param {string|null|undefined} str The input string containing email addresses + * @return {Array<{ label: string, email: string }>} List of parsed addresses + */ +export function parseEmailList(str) { + if (!str) { + return [] + } + + // Split on commas and semicolons (respecting quotes/angle brackets), + // then normalize to a comma-separated string for address-rfc2822. + const parts = splitOnDelimiters(str) + const normalized = parts.map((p) => p.trim()).filter(Boolean).join(', ') + + // First try: parse the whole normalized string (handles clean lists) + const results = tryParse(normalized) + if (results.length > 0) { + return results + } + + // Second try: parse each part individually. This handles cases like + // "not-an-email, alice@example.com" where the library rejects the whole string. + const list = [] + for (const part of parts) { + const trimmed = part.trim() + if (!trimmed) { + continue + } + + const parsed = tryParse(trimmed) + if (parsed.length > 0) { + list.push(...parsed) + } else if (trimmed.includes(' ') && trimmed.includes('@')) { + // Try splitting on spaces for space-separated bare emails + for (const word of trimmed.split(/\s+/)) { + list.push(...tryParse(word)) + } + } + } + return list +} From 6d20e3f06afb127e5e5f19fca8fd6a810747eb46 Mon Sep 17 00:00:00 2001 From: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> Date: Wed, 20 May 2026 18:42:54 +0200 Subject: [PATCH 044/228] fix(package): exclude dev config and patch files from release tarball eslint.config.mjs (flat config), stylelint.config.js, playwright.config.js, tsconfig.json, .patches/, patches.json, and patches.lock.json were all missing from .nextcloudignore and ended up in the v5.8.0 release tarball. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> --- .nextcloudignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.nextcloudignore b/.nextcloudignore index a9ec6055e6..4605780a89 100644 --- a/.nextcloudignore +++ b/.nextcloudignore @@ -11,6 +11,7 @@ codecov.yml /coverage /.editorconfig /.eslintrc.js +/eslint.config.mjs .git .git-blame-ignore-revs .gitattributes @@ -45,3 +46,9 @@ codecov.yml /webpack.* /vitest.config.* /patches +/.patches +/patches.json +/patches.lock.json +/playwright.config.js +/stylelint.config.js +/tsconfig.json From 525c3ab9d53f19082c137cc0050f4038c54f0b70 Mon Sep 17 00:00:00 2001 From: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> Date: Wed, 20 May 2026 20:06:16 +0200 Subject: [PATCH 045/228] fix(ui): add NcModal name prop to SmimeCertificateModal for accessibility NcModal requires either `name` or `labelId` so screen readers can announce the dialog. Use a dynamic value matching the two h2 headings already rendered in the list and import views. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> --- src/components/smime/SmimeCertificateModal.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/smime/SmimeCertificateModal.vue b/src/components/smime/SmimeCertificateModal.vue index bdfbe4383b..9e3386df87 100644 --- a/src/components/smime/SmimeCertificateModal.vue +++ b/src/components/smime/SmimeCertificateModal.vue @@ -4,7 +4,7 @@ -->