diff --git a/lib/__tests__/plugin-host-mail-read.test.ts b/lib/__tests__/plugin-host-mail-read.test.ts new file mode 100644 index 000000000..9db3e13b1 --- /dev/null +++ b/lib/__tests__/plugin-host-mail-read.test.ts @@ -0,0 +1,119 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import type { InstalledPlugin } from '../plugin-types'; +import type { IJMAPClient } from '../jmap/client-interface'; +import type { Email, Mailbox } from '../jmap/types'; +import { useAuthStore } from '@/stores/auth-store'; +import { useEmailStore } from '@/stores/email-store'; + +vi.mock('@/stores/toast-store', () => ({ + toast: { success: vi.fn(), error: vi.fn(), info: vi.fn(), warning: vi.fn() }, +})); + +import { dispatchApiCall } from '../plugin-sandbox/host-api'; + +function plugin(perms: string[]): InstalledPlugin { + return { + id: 'reader', name: 'Reader', version: '1.0.0', author: 'test', description: '', + type: 'hook', permissions: perms, grantedPermissions: perms, entrypoint: 'index.js', + enabled: true, status: 'running', settings: {}, + }; +} + +const READER = plugin(['email:read']); +const call = (p: InstalledPlugin, method: string, ...args: unknown[]) => dispatchApiCall(p, method, args); + +function mail(id: string, over: Partial = {}): Email { + return { + id, threadId: `t-${id}`, mailboxIds: { inbox: true }, keywords: { $seen: true }, + from: [{ name: 'Bob', email: 'bob@example.com' }], to: [{ email: 'ann@example.com' }], + subject: `Subject ${id}`, receivedAt: '2026-05-01T10:00:00Z', preview: 'preview', size: 1234, + hasAttachment: false, + ...over, + } as Email; +} + +const inbox = { id: 'inbox', name: 'Inbox', role: 'inbox', totalEmails: 12, unreadEmails: 3, sortOrder: 0 } as Mailbox; +const shared = { + id: 'acc2:box', originalId: 'box', name: 'Team', role: undefined, parentId: undefined, + totalEmails: 1, unreadEmails: 0, isShared: true, accountId: 'acc2', sortOrder: 1, +} as unknown as Mailbox; + +let client: Record>; + +beforeEach(() => { + client = { + getEmail: vi.fn(async (id: string) => (id === 'm1' + ? mail('m1', { textBody: [{ partId: '1' }], bodyValues: { 1: { value: 'Full body text' } } } as unknown as Partial) + : null)), + getEmails: vi.fn(async () => ({ emails: [mail('a'), mail('b')], hasMore: false, total: 2 })), + getMailboxes: vi.fn(async () => [inbox]), + }; + useAuthStore.setState({ client: client as unknown as IJMAPClient }); + useEmailStore.setState({ mailboxes: [inbox, shared], selectedEmail: null }); +}); + +afterEach(() => { + useAuthStore.setState({ client: null }); + useEmailStore.setState({ mailboxes: [], selectedEmail: null }); +}); + +describe('api.email.get', () => { + it('returns any message with its body, not only the open one', async () => { + const view = await call(READER, 'email.get', 'm1') as { id: string; text: string; from: unknown }; + expect(view).toEqual(expect.objectContaining({ + id: 'm1', subject: 'Subject m1', text: 'Full body text', from: [{ name: 'Bob', email: 'bob@example.com' }], + })); + expect(client.getEmail).toHaveBeenCalledWith('m1'); + expect(await call(READER, 'email.get', 'gone')).toBeNull(); + }); + + it('uses the open message when its body is loaded', async () => { + useEmailStore.setState({ selectedEmail: mail('open', { bodyValues: { 1: { value: 'x' } }, textBody: [{ partId: '1' }] } as unknown as Partial) }); + expect(await call(READER, 'email.get', 'open')).toEqual(expect.objectContaining({ id: 'open', text: 'x' })); + expect(client.getEmail).not.toHaveBeenCalled(); + }); + + it('needs email:read and a string id', async () => { + await expect(call(plugin([]), 'email.get', 'm1')).rejects.toThrow(/lacks permission "email:read"/); + await expect(call(READER, 'email.get', { id: 'm1' })).rejects.toThrow(/emailId must be a string/); + expect(client.getEmail).not.toHaveBeenCalled(); + }); +}); + +describe('api.email.list', () => { + it('returns summaries of a folder page', async () => { + const list = await call(READER, 'email.list', { mailboxId: 'inbox', limit: 2, position: 10 }) as { id: string }[]; + expect(list.map((e) => e.id)).toEqual(['a', 'b']); + expect(list[0]).not.toHaveProperty('text'); + expect(client.getEmails).toHaveBeenCalledWith('inbox', undefined, 2, 10); + }); + + it('asks the owning account for a shared folder', async () => { + await call(READER, 'email.list', { mailboxId: 'acc2:box' }); + expect(client.getEmails).toHaveBeenCalledWith('box', 'acc2', 50, 0); + }); + + it('caps the page at 100 and refuses unknown folders', async () => { + await expect(call(READER, 'email.list', { limit: 101 })).rejects.toThrow(/limit must be a whole number from 1 to 100/); + await expect(call(READER, 'email.list', { mailboxId: 'nope' })).rejects.toThrow(/no mailbox with id "nope"/); + await expect(call(READER, 'email.list', { mailbox: 'inbox' })).rejects.toThrow(/unknown property "mailbox"/); + await expect(call(plugin([]), 'email.list')).rejects.toThrow(/lacks permission "email:read"/); + expect(client.getEmails).not.toHaveBeenCalled(); + }); +}); + +describe('api.mailboxes.list', () => { + it('returns plain folder views', async () => { + expect(await call(READER, 'mailboxes.list')).toEqual([ + { id: 'inbox', name: 'Inbox', role: 'inbox', parentId: null, totalEmails: 12, unreadEmails: 3 }, + { id: 'acc2:box', name: 'Team', role: null, parentId: null, totalEmails: 1, unreadEmails: 0 }, + ]); + await expect(call(plugin(['email:write']), 'mailboxes.list')).rejects.toThrow(/lacks permission "email:read"/); + }); + + it('asks the server when the app has not loaded folders yet', async () => { + useEmailStore.setState({ mailboxes: [] }); + expect(await call(READER, 'mailboxes.list')).toHaveLength(1); + expect(client.getMailboxes).toHaveBeenCalled(); + }); +}); diff --git a/lib/plugin-sandbox/host-api-mail.ts b/lib/plugin-sandbox/host-api-mail.ts new file mode 100644 index 000000000..6ea809f8c --- /dev/null +++ b/lib/plugin-sandbox/host-api-mail.ts @@ -0,0 +1,76 @@ +// Host side of reading mail for plugins: api.email.get / list and +// api.mailboxes.list. Reads go to the server through the JMAP client and +// never change what the user has open. +// +// Messages are read from the signed-in account itself; mail in folders shared +// from other accounts is not reached by id here. + +import type { EmailReadView, EmailSummary, MailboxView } from '../plugin-types'; +import type { Email, Mailbox } from '../jmap/types'; +import type { IJMAPClient } from '../jmap/client-interface'; +import { useEmailStore } from '@/stores/email-store'; +import { emailSummary, emailToReadView } from '../plugin-projection'; +import { readId, readInteger, readObject, sessionClient } from './host-api-input'; + +/** Most messages one api.email.list call returns. */ +export const MAX_PLUGIN_EMAIL_PAGE = 100; + +/** The open message when it is this one and its body is loaded, else the server's copy. */ +async function loadEmail(client: IJMAPClient, emailId: string): Promise { + const { selectedEmail } = useEmailStore.getState(); + if (selectedEmail?.id === emailId && selectedEmail.bodyValues && !selectedEmail.accountId) { + return selectedEmail; + } + return client.getEmail(emailId); +} + +export async function doEmailGet(id: unknown): Promise { + const method = 'email.get'; + const emailId = readId(id, method, 'emailId'); + const email = await loadEmail(sessionClient(method), emailId); + return email ? emailToReadView(email) : null; +} + +function mailboxToView(mailbox: Mailbox): MailboxView { + return { + id: mailbox.id, + name: mailbox.name, + role: mailbox.role ?? null, + parentId: mailbox.parentId ?? null, + totalEmails: mailbox.totalEmails ?? 0, + unreadEmails: mailbox.unreadEmails ?? 0, + }; +} + +async function knownMailboxes(client: IJMAPClient): Promise { + const { mailboxes } = useEmailStore.getState(); + return mailboxes.length > 0 ? mailboxes : client.getMailboxes(); +} + +export async function doMailboxesList(): Promise { + return (await knownMailboxes(sessionClient('mailboxes.list'))).map(mailboxToView); +} + +/** + * One page of a folder, newest first in the user's list order, as message + * summaries (no body). Without mailboxId it pages through all mail. + */ +export async function doEmailList(options: unknown): Promise { + const method = 'email.list'; + const opts = readObject(options, method, 'options', ['mailboxId', 'limit', 'position'], true); + const limit = opts.limit === undefined ? 50 : readInteger(opts.limit, method, 'limit', 1, MAX_PLUGIN_EMAIL_PAGE); + const position = opts.position === undefined ? 0 : readInteger(opts.position, method, 'position', 0, 1_000_000); + const client = sessionClient(method); + let jmapMailboxId: string | undefined; + let accountId: string | undefined; + if (opts.mailboxId !== undefined) { + const id = readId(opts.mailboxId, method, 'mailboxId'); + const mailbox = (await knownMailboxes(client)).find((m) => m.id === id); + if (!mailbox) throw new Error(`${method}: no mailbox with id "${id}"`); + // Shared folders carry a namespaced id in the app; the server knows the original. + jmapMailboxId = mailbox.originalId || mailbox.id; + accountId = mailbox.isShared ? mailbox.accountId : undefined; + } + const { emails } = await client.getEmails(jmapMailboxId, accountId, limit, position); + return emails.map(emailSummary); +} diff --git a/lib/plugin-sandbox/host-api.ts b/lib/plugin-sandbox/host-api.ts index 796337398..ab7ac301b 100644 --- a/lib/plugin-sandbox/host-api.ts +++ b/lib/plugin-sandbox/host-api.ts @@ -41,6 +41,7 @@ import { doIdentitiesUpdate, doTemplatesCreate, doTemplatesDelete, doTemplatesGet, doTemplatesList, doTemplatesUpdate, doVacationGet, doVacationSet, } from './host-api-settings'; +import { doEmailGet, doEmailList, doMailboxesList } from './host-api-mail'; /** * Methods only callable from the privileged (same-origin) tier. These expose @@ -209,6 +210,10 @@ export const PERM_PER_METHOD: Readonly> = { // out-of-office reply 'vacation.get': 'vacation:read', 'vacation.set': 'vacation:write', + // reading mail: one message with its text, a page of a folder, the folders + 'email.get': 'email:read', + 'email.list': 'email:read', + 'mailboxes.list': 'email:read', }; // ─── Cross-origin allow-list (mirrors lib/plugin-api.ts) ────── @@ -1895,6 +1900,10 @@ export async function dispatchApiCall( case 'vacation.get': return doVacationGet(); case 'vacation.set': return doVacationSet(args[0]); + case 'email.get': return doEmailGet(args[0]); + case 'email.list': return doEmailList(args[0]); + case 'mailboxes.list': return doMailboxesList(); + default: throw new Error(`Unhandled method "${method}"`); } diff --git a/lib/plugin-sandbox/runtime.tsx b/lib/plugin-sandbox/runtime.tsx index 7828bd3b2..7205ff6da 100644 --- a/lib/plugin-sandbox/runtime.tsx +++ b/lib/plugin-sandbox/runtime.tsx @@ -31,8 +31,9 @@ import { themeSnapshotToCSS, type ThemeSnapshot } from './host-theme'; import { interpolate, lookup, textDirection, type LocaleTables } from './plugin-i18n'; import { isInlineSlot, MAX_INLINE_SLOT_WIDTH } from './slot-layout'; import type { - CalendarEventInput, CalendarEventView, CalendarView, IdentityPatch, IdentityView, SlotName, - TaskInput, TaskView, TemplateInput, TemplateView, VacationInput, VacationView, + CalendarEventInput, CalendarEventView, CalendarView, EmailReadView, EmailSummary, IdentityPatch, + IdentityView, MailboxView, SlotName, TaskInput, TaskView, TemplateInput, TemplateView, VacationInput, + VacationView, } from '../plugin-types'; import { AddressBook, ContactCard } from '../jmap/types'; import { EncryptionAtRestConfig, PublicKeyInput } from '@/stores/account-security-store'; @@ -425,14 +426,32 @@ function buildPluginApi(manifest: PluginManifest) { downloadFile: (opts: { content: string; filename: string; contentType?: string }) => callApi('ui.downloadFile', [opts]) as Promise, }, - // Email keyword mutations (permission: email:write). Keywords follow JMAP - // syntax, e.g. '$category-promotions' or '$label:'. + // Reading mail (permission: email:read) and keyword mutations + // (permission: email:write). Keywords follow JMAP syntax, e.g. + // '$category-promotions' or '$label:'. email: { + /** + * One message with its plain text, headers and source, or null when + * there is none with that id. Any message of the account, not only the + * open one. + */ + get: (emailId: string) => callApi('email.get', [emailId]) as Promise, + /** + * A page of a folder (or of all mail without mailboxId), in the user's + * list order: summaries without the body. limit 1-100 (default 50). + */ + list: (options?: { mailboxId?: string; limit?: number; position?: number }) => + callApi('email.list', [options]) as Promise, setKeyword: (emailId: string, keyword: string, accountId?: string) => callApi('email.setKeyword', [emailId, keyword, accountId]) as Promise, removeKeyword: (emailId: string, keyword: string, accountId?: string) => callApi('email.removeKeyword', [emailId, keyword, accountId]) as Promise, }, + // Folders (permission: email:read). + mailboxes: { + /** Every folder with its role, parent and message counts. */ + list: () => callApi('mailboxes.list', []) as Promise, + }, // Native sidebar tag definitions. Definition reads/writes use the existing // settings permissions; server discovery and message counts use email:read. // add() is intentionally append-only. reorder() requires a complete diff --git a/locales/ar/common.json b/locales/ar/common.json index 24f3f9268..69e42e6d4 100644 --- a/locales/ar/common.json +++ b/locales/ar/common.json @@ -4034,7 +4034,7 @@ "high_risk": "تتطلب الحذر", "email_read": { "title": "قراءة بريدك", - "body": "عرض المرسلين والمستلمين والمواضيع والنص الكامل للرسائل التي تفتحها ولقائمة الرسائل." + "body": "عرض المرسلين والمستلمين والمواضيع والنص الكامل لأي رسالة في حسابك، ومجلداتك." }, "email_write": { "title": "تغيير الوسوم والعلامات على بريدك", diff --git a/locales/ca/common.json b/locales/ca/common.json index d9ea2a1b8..3faa1e74a 100644 --- a/locales/ca/common.json +++ b/locales/ca/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Cal anar amb compte", "email_read": { "title": "Llegir el vostre correu", - "body": "Veure remitents, destinataris, assumptes i el text complet dels missatges que obriu i de la llista de missatges." + "body": "Veure remitents, destinataris, assumptes i el text complet de qualsevol missatge del vostre compte, i les vostres carpetes." }, "email_write": { "title": "Canviar les etiquetes i marques del correu", diff --git a/locales/cs/common.json b/locales/cs/common.json index aec9ada4e..40930c54e 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Vyžaduje opatrnost", "email_read": { "title": "Číst vaši poštu", - "body": "Vidí odesílatele, příjemce, předměty a celý text zpráv, které otevřete, i seznamu zpráv." + "body": "Vidí odesílatele, příjemce, předměty a celý text kterékoli zprávy ve vašem účtu i vaše složky." }, "email_write": { "title": "Měnit štítky a příznaky pošty", diff --git a/locales/da/common.json b/locales/da/common.json index e099ca36a..c0656d684 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Kræver omtanke", "email_read": { "title": "Læse dine e-mails", - "body": "Se afsendere, modtagere, emner og den fulde tekst i de beskeder, du åbner, og i beskedlisten." + "body": "Se afsendere, modtagere, emner og den fulde tekst i alle beskeder på din konto samt dine mapper." }, "email_write": { "title": "Ændre tags og markeringer på dine e-mails", diff --git a/locales/de/common.json b/locales/de/common.json index 2ba2ed0de..ee55d757a 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Mit Vorsicht", "email_read": { "title": "Ihre E-Mails lesen", - "body": "Absender, Empfänger, Betreff und den vollständigen Text der Nachrichten sehen, die Sie öffnen, sowie der Nachrichtenliste." + "body": "Absender, Empfänger, Betreff und den vollständigen Text jeder Nachricht in Ihrem Konto sowie Ihre Ordner sehen." }, "email_write": { "title": "Labels und Markierungen Ihrer E-Mails ändern", diff --git a/locales/en/common.json b/locales/en/common.json index 942d372a4..f4a543f11 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Needs care", "email_read": { "title": "Read your email", - "body": "See senders, recipients, subjects and the full text of messages you open and of the message list." + "body": "See senders, recipients, subjects and the full text of any message in your account, and your folders." }, "email_write": { "title": "Change tags and flags on your email", diff --git a/locales/es/common.json b/locales/es/common.json index 4a7136e37..b1d18d971 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Con cuidado", "email_read": { "title": "Leer tu correo", - "body": "Ver remitentes, destinatarios, asuntos y el texto completo de los mensajes que abres y de la lista de mensajes." + "body": "Ver remitentes, destinatarios, asuntos y el texto completo de cualquier mensaje de tu cuenta, y tus carpetas." }, "email_write": { "title": "Cambiar etiquetas y marcas de tu correo", diff --git a/locales/fa/common.json b/locales/fa/common.json index 2343dac11..93acc0672 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -4034,7 +4034,7 @@ "high_risk": "نیازمند احتیاط", "email_read": { "title": "خواندن ایمیل‌های شما", - "body": "دیدن فرستندگان، گیرندگان، موضوع‌ها و متن کامل پیام‌هایی که باز می‌کنید و فهرست پیام‌ها." + "body": "دیدن فرستندگان، گیرندگان، موضوع‌ها و متن کامل هر پیامی در حساب شما، و پوشه‌های شما." }, "email_write": { "title": "تغییر برچسب‌ها و پرچم‌های ایمیل‌های شما", diff --git a/locales/fr/common.json b/locales/fr/common.json index 3527665e8..d78fb6cf9 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Prudence", "email_read": { "title": "Lire vos e-mails", - "body": "Voir les expéditeurs, les destinataires, les objets et le texte complet des messages que vous ouvrez et de la liste des messages." + "body": "Voir les expéditeurs, les destinataires, les objets et le texte complet de n'importe quel message de votre compte, ainsi que vos dossiers." }, "email_write": { "title": "Modifier les étiquettes et indicateurs de vos e-mails", diff --git a/locales/he/common.json b/locales/he/common.json index 10b638bb2..1e2daf712 100644 --- a/locales/he/common.json +++ b/locales/he/common.json @@ -4035,7 +4035,7 @@ "high_risk": "דורש זהירות", "email_read": { "title": "קריאת הדואר שלכם", - "body": "צפייה בשולחים, בנמענים, בנושאים ובטקסט המלא של הודעות שאתם פותחים ושל רשימת ההודעות." + "body": "צפייה בשולחים, בנמענים, בנושאים ובטקסט המלא של כל הודעה בחשבון שלכם, ובתיקיות שלכם." }, "email_write": { "title": "שינוי תגים וסימונים בדואר שלכם", diff --git a/locales/hu/common.json b/locales/hu/common.json index 550c4292e..9cab011e0 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Óvatosan", "email_read": { "title": "Leveleid olvasása", - "body": "Látja a megnyitott üzenetek és az üzenetlista feladóit, címzettjeit, tárgyát és teljes szövegét." + "body": "Látja a fiókod bármely üzenetének feladóját, címzettjeit, tárgyát és teljes szövegét, valamint a mappáidat." }, "email_write": { "title": "Címkék és jelölők módosítása a leveleiden", diff --git a/locales/it/common.json b/locales/it/common.json index a738930ef..9e9c38246 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Con cautela", "email_read": { "title": "Leggere la tua posta", - "body": "Vedere mittenti, destinatari, oggetti e il testo completo dei messaggi che apri e dell'elenco dei messaggi." + "body": "Vedere mittenti, destinatari, oggetti e il testo completo di qualsiasi messaggio del tuo account, e le tue cartelle." }, "email_write": { "title": "Modificare etichette e contrassegni della tua posta", diff --git a/locales/ja/common.json b/locales/ja/common.json index b97e9101e..38aaae9ee 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -4034,7 +4034,7 @@ "high_risk": "注意が必要", "email_read": { "title": "メールを読む", - "body": "開いたメッセージとメッセージ一覧の差出人、宛先、件名、本文全体を見ることができます。" + "body": "アカウント内のすべてのメッセージの差出人、宛先、件名、本文全体と、フォルダを見ることができます。" }, "email_write": { "title": "メールのタグとフラグを変更", diff --git a/locales/ko/common.json b/locales/ko/common.json index 195cf5797..6cd559931 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -4034,7 +4034,7 @@ "high_risk": "주의 필요", "email_read": { "title": "메일 읽기", - "body": "열어 본 메시지와 메시지 목록의 보낸 사람, 받는 사람, 제목, 전체 본문을 볼 수 있어요." + "body": "계정에 있는 모든 메시지의 보낸 사람, 받는 사람, 제목, 전체 본문과 폴더를 볼 수 있어요." }, "email_write": { "title": "메일의 태그와 플래그 변경", diff --git a/locales/lv/common.json b/locales/lv/common.json index cf298dfeb..deb525349 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Nepieciešama piesardzība", "email_read": { "title": "Lasīt jūsu pastu", - "body": "Redzēt sūtītājus, adresātus, tēmas un visu tekstu vēstulēm, ko atverat, un vēstulēm sarakstā." + "body": "Redzēt sūtītājus, adresātus, tēmas un visu tekstu jebkurai vēstulei jūsu kontā, kā arī jūsu mapes." }, "email_write": { "title": "Mainīt vēstuļu tagus un karodziņus", diff --git a/locales/mn/common.json b/locales/mn/common.json index 21ba2eed0..fd03bcba9 100644 --- a/locales/mn/common.json +++ b/locales/mn/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Болгоомжтой хандах", "email_read": { "title": "Таны имэйлийг унших", - "body": "Таны нээсэн имэйлүүд болон имэйлийн жагсаалтын илгээгч, хүлээн авагч, гарчиг, бүрэн текстийг харах." + "body": "Таны бүртгэл дэх аль ч имэйлийн илгээгч, хүлээн авагч, гарчиг, бүрэн текст болон таны хавтаснуудыг харах." }, "email_write": { "title": "Имэйлийн таг болон тугийг өөрчлөх", diff --git a/locales/nb/common.json b/locales/nb/common.json index 0ea3afcd0..714a734e8 100644 --- a/locales/nb/common.json +++ b/locales/nb/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Krever varsomhet", "email_read": { "title": "Lese e-posten din", - "body": "Se avsendere, mottakere, emner og hele teksten i meldingene du åpner og i meldingslisten." + "body": "Se avsendere, mottakere, emner og hele teksten i alle meldinger på kontoen din, og mappene dine." }, "email_write": { "title": "Endre etiketter og flagg på e-posten din", diff --git a/locales/nl/common.json b/locales/nl/common.json index 12acefe93..b1c14453d 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Let op", "email_read": { "title": "Je e-mail lezen", - "body": "Afzenders, ontvangers, onderwerpen en de volledige tekst zien van berichten die je opent en van de berichtenlijst." + "body": "Afzenders, ontvangers, onderwerpen en de volledige tekst zien van elk bericht in je account, en je mappen." }, "email_write": { "title": "Labels en markeringen van je e-mail wijzigen", diff --git a/locales/pl/common.json b/locales/pl/common.json index c0d941186..63ac73b11 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Wymaga ostrożności", "email_read": { "title": "Czytanie poczty", - "body": "Widzi nadawców, odbiorców, tematy i pełną treść otwieranych wiadomości oraz listy wiadomości." + "body": "Widzi nadawców, odbiorców, tematy i pełną treść każdej wiadomości na Twoim koncie oraz Twoje foldery." }, "email_write": { "title": "Zmiana etykiet i flag w poczcie", diff --git a/locales/pt/common.json b/locales/pt/common.json index 4b0c0702b..5a961d810 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Requer atenção", "email_read": { "title": "Ler seus e-mails", - "body": "Ver remetentes, destinatários, assuntos e o texto completo das mensagens que você abre e da lista de mensagens." + "body": "Ver remetentes, destinatários, assuntos e o texto completo de qualquer mensagem da sua conta, e suas pastas." }, "email_write": { "title": "Alterar etiquetas e marcações dos seus e-mails", diff --git a/locales/ro/common.json b/locales/ro/common.json index 9252a4761..00931022e 100644 --- a/locales/ro/common.json +++ b/locales/ro/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Necesită atenție", "email_read": { "title": "Citirea e-mailurilor", - "body": "Vede expeditorii, destinatarii, subiectele și textul complet al mesajelor pe care le deschideți și al listei de mesaje." + "body": "Vede expeditorii, destinatarii, subiectele și textul complet al oricărui mesaj din contul dvs., precum și dosarele dvs." }, "email_write": { "title": "Modificarea etichetelor și marcajelor e-mailurilor", diff --git a/locales/ru/common.json b/locales/ru/common.json index 359f85ecb..28c13937c 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Требует осторожности", "email_read": { "title": "Читать вашу почту", - "body": "Видеть отправителей, получателей, темы и полный текст открытых писем и писем в списке сообщений." + "body": "Видеть отправителей, получателей, темы и полный текст любого письма в вашей учётной записи, а также ваши папки." }, "email_write": { "title": "Менять теги и флажки писем", diff --git a/locales/sk/common.json b/locales/sk/common.json index e63d2a6e9..b5463d6ae 100644 --- a/locales/sk/common.json +++ b/locales/sk/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Vyžaduje opatrnosť", "email_read": { "title": "Čítať vašu poštu", - "body": "Vidí odosielateľov, príjemcov, predmety a celý text správ, ktoré otvoríte, aj zoznamu správ." + "body": "Vidí odosielateľov, príjemcov, predmety a celý text ktorejkoľvek správy vo vašom účte aj vaše priečinky." }, "email_write": { "title": "Meniť štítky a príznaky pošty", diff --git a/locales/tr/common.json b/locales/tr/common.json index 18127549b..69d47d3b0 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Dikkat gerektirir", "email_read": { "title": "E-postalarınızı okuma", - "body": "Açtığınız iletilerin ve ileti listesindekilerin gönderenlerini, alıcılarını, konularını ve tam metnini görme." + "body": "Hesabınızdaki herhangi bir iletinin gönderenlerini, alıcılarını, konusunu ve tam metnini, ayrıca klasörlerinizi görme." }, "email_write": { "title": "E-postalarınızdaki etiketleri ve bayrakları değiştirme", diff --git a/locales/uk/common.json b/locales/uk/common.json index d28f9e356..aeb6cf702 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -4034,7 +4034,7 @@ "high_risk": "Потребує обережності", "email_read": { "title": "Читати вашу пошту", - "body": "Бачити відправників, одержувачів, теми й повний текст відкритих листів і листів у списку повідомлень." + "body": "Бачити відправників, одержувачів, теми й повний текст будь-якого листа у вашому обліковому записі, а також ваші папки." }, "email_write": { "title": "Змінювати теги й позначки листів", diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index 787a71aba..d3d9157c1 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -4034,7 +4034,7 @@ "high_risk": "需留意", "email_read": { "title": "讀取您的郵件", - "body": "查看您開啟的郵件和郵件清單中的寄件者、收件者、主旨和完整內文。" + "body": "查看您帳戶中任何郵件的寄件者、收件者、主旨和完整內文,以及您的信件匣。" }, "email_write": { "title": "變更郵件的標籤和旗標", diff --git a/locales/zh/common.json b/locales/zh/common.json index ec47531a7..303ae4f19 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -4034,7 +4034,7 @@ "high_risk": "需谨慎", "email_read": { "title": "读取您的邮件", - "body": "查看您打开的邮件和邮件列表中的发件人、收件人、主题和完整正文。" + "body": "查看您账户中任何邮件的发件人、收件人、主题和完整正文,以及您的文件夹。" }, "email_write": { "title": "更改邮件的标签和旗标",