From 5efe718cf0c4af916ca660c344355c5c56a1cd34 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Mon, 12 Oct 2026 01:45:35 +0200 Subject: [PATCH] feat: order Mountain View's message menu like Gmail's and add Find emails from --- .../email-context-menu-mountain-view.test.tsx | 67 +++ components/email/email-context-menu.tsx | 451 +++++++++++------- components/email/email-list.tsx | 6 + components/mail/mail-app.tsx | 1 + locales/ar/common.json | 3 +- locales/ca/common.json | 3 +- locales/cs/common.json | 3 +- locales/da/common.json | 3 +- locales/de/common.json | 3 +- locales/en/common.json | 3 +- locales/es/common.json | 3 +- locales/fa/common.json | 3 +- locales/fr/common.json | 3 +- locales/he/common.json | 3 +- locales/hu/common.json | 3 +- locales/it/common.json | 3 +- locales/ja/common.json | 3 +- locales/ko/common.json | 3 +- locales/lv/common.json | 3 +- locales/mn/common.json | 3 +- locales/nb/common.json | 3 +- locales/nl/common.json | 3 +- locales/pl/common.json | 3 +- locales/pt/common.json | 3 +- locales/ro/common.json | 3 +- locales/ru/common.json | 3 +- locales/sk/common.json | 3 +- locales/tr/common.json | 3 +- locales/uk/common.json | 3 +- locales/zh-TW/common.json | 3 +- locales/zh/common.json | 3 +- 31 files changed, 399 insertions(+), 207 deletions(-) create mode 100644 components/email/__tests__/email-context-menu-mountain-view.test.tsx diff --git a/components/email/__tests__/email-context-menu-mountain-view.test.tsx b/components/email/__tests__/email-context-menu-mountain-view.test.tsx new file mode 100644 index 000000000..bc79d6205 --- /dev/null +++ b/components/email/__tests__/email-context-menu-mountain-view.test.tsx @@ -0,0 +1,67 @@ +import { render, screen, fireEvent } from '@testing-library/react'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import type { Email, Mailbox } from '@/lib/jmap/types'; +import { useSettingsStore } from '@/stores/settings-store'; + +vi.mock('@/components/plugins/plugin-slot', () => ({ PluginSlot: () => null })); +vi.mock('../rules-menu', () => ({ RulesContextSubMenu: () => null })); +vi.mock('@/hooks/use-copy-link', () => ({ useCopyLink: () => vi.fn() })); + +const { EmailContextMenu } = await import('../email-context-menu'); + +const rights = { mayAddItems: true } as Mailbox['myRights']; +const mailboxes = [ + { id: 'inbox', name: 'Inbox', role: 'inbox', myRights: rights }, + { id: 'archive', name: 'Archive', role: 'archive', myRights: rights }, +] as unknown as Mailbox[]; +const email = { + id: 'e1', + keywords: { $seen: true }, + mailboxIds: { inbox: true }, + from: [{ name: 'Sarah Kim', email: 'sarah@example.com' }], +} as unknown as Email; + +function renderMenu(props: Partial> = {}) { + return render( + {}} + menuRef={{ current: null }} + mailboxes={mailboxes} + selectedMailbox="inbox" + onArchive={() => {}} + onDelete={() => {}} + {...props} + />, + ); +} + +/** The menu's entries by test id, top to bottom. */ +const order = () => [...document.querySelectorAll('[data-testid^="ctx-"]')].map((e) => e.getAttribute('data-testid')); + +describe('message menu under Mountain View', () => { + beforeEach(() => useSettingsStore.setState({ interfaceLayout: 'mountain-view' } as never)); + + it("puts Mark as read with Archive and Delete, before Move to, as Gmail does", () => { + renderMenu(); + const ids = order(); + expect(ids.indexOf('ctx-delete')).toBeLessThan(ids.indexOf('ctx-mark-unread')); + expect(ids.indexOf('ctx-mark-unread')).toBeLessThan(ids.indexOf('ctx-move-to')); + }); + + it('finds the mail from the sender', () => { + const onFindFromSender = vi.fn(); + renderMenu({ onFindFromSender }); + fireEvent.click(screen.getByTestId('ctx-find-from')); + expect(onFindFromSender).toHaveBeenCalledTimes(1); + }); + + it('keeps the default order outside Mountain View', () => { + useSettingsStore.setState({ interfaceLayout: 'default' } as never); + renderMenu({ onFindFromSender: vi.fn() }); + const ids = order(); + expect(ids.indexOf('ctx-move-to')).toBeLessThan(ids.indexOf('ctx-mark-unread')); + }); +}); diff --git a/components/email/email-context-menu.tsx b/components/email/email-context-menu.tsx index 15e61ea51..3c732be1a 100644 --- a/components/email/email-context-menu.tsx +++ b/components/email/email-context-menu.tsx @@ -2,6 +2,7 @@ import { useMemo } from "react"; import { useTranslations } from "next-intl"; +import { useSettingsStore } from "@/stores/settings-store"; import { Email, Mailbox } from "@/lib/jmap/types"; import { ContextMenu, @@ -36,6 +37,7 @@ import { Link as LinkIcon, MessagesSquare, Copy, + Search, } from "@/components/icons"; import { buildMailPath } from "@/lib/deep-links"; import { useCopyLink } from "@/hooks/use-copy-link"; @@ -82,6 +84,8 @@ interface EmailContextMenuProps { onReplyAll?: () => void; onForward?: () => void; onForwardAsAttachment?: () => void; + /** Mountain View: search for the mail this sender has sent. */ + onFindFromSender?: () => void; onMarkAsRead?: (read: boolean) => void; onToggleStar?: () => void; onTogglePinned?: () => void; @@ -148,6 +152,7 @@ export function EmailContextMenu({ onReplyAll, onForward, onForwardAsAttachment, + onFindFromSender, onMarkAsRead, onToggleStar, onTogglePinned, @@ -173,6 +178,7 @@ export function EmailContextMenu({ onCopyToAccount, }: EmailContextMenuProps) { const t = useTranslations("context_menu"); + const mountainView = useSettingsStore((state) => state.interfaceLayout === "mountain-view"); const tSidebar = useTranslations("sidebar"); const tEmailViewer = useTranslations("email_viewer"); const tDeepLink = useTranslations("deep_link"); @@ -260,6 +266,238 @@ export function EmailContextMenu({ // Stable, so the Rules entry does not re-resolve its account every render. const ruleSenderEmails = useMemo(() => ruleEmails ?? [email], [ruleEmails, email]); + // The menu's entries, composed below in the default layout's order or, + // under Mountain View, in Gmail's. + const replyBlock = ( + <> + {/* Single email actions - Reply, Reply All, Forward */} + {!isScheduled && !showBatchActions && ( + <> + handleAction(onReply!)} + disabled={!onReply} + /> + handleAction(onReplyAll!)} + disabled={!onReplyAll} + /> + handleAction(onForward!)} + disabled={!onForward} + /> + handleAction(onForwardAsAttachment!)} + disabled={!onForwardAsAttachment || !email.blobId} + /> + + + )} + + ); + const permalinkBlock = ( + <> + {/* Permalinks (#733). The conversation entry only appears when the + message actually belongs to a thread worth linking to. */} + {!showBatchActions && ( + <> + handleAction(() => { + void copyLink(buildMailPath({ mailboxId: null, emailId: email.id, threadId: null })); + })} + /> + {email.threadId && ( + handleAction(() => { + void copyLink(buildMailPath({ mailboxId: null, emailId: null, threadId: email.threadId! })); + })} + /> + )} + + + )} + + ); + const archiveItem = ( + <> + {/* Archive */} + + handleAction(showBatchActions ? onBatchArchive! : onArchive!) + } + disabled={showBatchActions ? !onBatchArchive : !onArchive} + /> + + ); + const deleteItem = ( + <> + {/* Delete */} + + handleAction(showBatchActions ? onBatchDelete! : onDelete!) + } + disabled={showBatchActions ? !onBatchDelete : !onDelete} + destructive={!mountainView} + /> + + ); + const readItem = ( + <> + {/* Mark as read/unread */} + + handleAction(() => + showBatchActions + ? onBatchMarkAsRead?.(isUnread) + : onMarkAsRead?.(isUnread) + ) + } + /> + + ); + const moveItem = ( + <> + {/* Move to submenu */} + {moveTree.length > 0 && ( + + {renderMailboxNodes(moveTree, moveTargetIds, "move-to:", (mailboxId) => + showBatchActions + ? onBatchMoveToMailbox?.(mailboxId) + : onMoveToMailbox?.(mailboxId) + )} + + )} + + ); + const copyItem = ( + <> + {/* Copy to another connected account, keeping the originals */} + {onCopyToAccount && copyTrees.length > 0 && ( + + {copyTrees.map((target) => ( +
+ {target.label} + {renderMailboxNodes(target.tree, target.targetIds, `copy-to:${target.accountId}:`, (mailboxId) => + onCopyToAccount(target.accountId, mailboxId) + )} +
+ ))} +
+ )} + + ); + const rulesItem = ( + <> + {/* Rules: filter rules made from the sender(s), saved as Sieve */} + + + ); + const starItem = ( + <> + {/* Star/Unstar - only for single email */} + {!showBatchActions && ( + handleAction(onToggleStar!)} + disabled={!onToggleStar} + testId={isStarred ? "ctx-unstar" : "ctx-star"} + /> + )} + + ); + const pinItem = ( + <> + {/* Pin/Unpin - only for single email; pinned mails float to the top of the list */} + {!showBatchActions && onTogglePinned && ( + handleAction(onTogglePinned)} + /> + )} + + ); + const tagItem = ( + <> + {/* Set tag submenu - the row's own tags, or the whole selection's (#1077) */} + {(!showBatchActions || onBatchToggleTag) && ( + +
+ {showBatchActions ? ( + onBatchToggleTag?.(tagId)} + /> + ) : ( + onSetTag?.(tagId)} + /> + )} +
+
+ )} + + ); + const spamBlock = ( + <> + {/* Spam - contextual based on folder; pointless on own outgoing mail */} + {spamApplicable && ( + <> + + + + handleAction( + showBatchActions + ? (isInJunkFolder ? onBatchUndoSpam! : onBatchMarkAsSpam!) + : (isInJunkFolder ? onUndoSpam! : onMarkAsSpam!) + ) + } + disabled={showBatchActions ? (isInJunkFolder ? !onBatchUndoSpam : !onBatchMarkAsSpam) : (isInJunkFolder ? !onUndoSpam : !onMarkAsSpam)} + destructive={!isInJunkFolder && !mountainView} + /> + + )} + + ); + const sender = email.from?.[0]; + const findBlock = !showBatchActions && onFindFromSender && sender?.email ? ( + <> + + handleAction(onFindFromSender)} + /> + + ) : null; + return ( )} - {/* Single email actions - Reply, Reply All, Forward */} - {!isScheduled && !showBatchActions && ( + {mountainView ? ( <> - handleAction(onReply!)} - disabled={!onReply} - /> - handleAction(onReplyAll!)} - disabled={!onReplyAll} - /> - handleAction(onForward!)} - disabled={!onForward} - /> - handleAction(onForwardAsAttachment!)} - disabled={!onForwardAsAttachment || !email.blobId} - /> + {replyBlock} + {archiveItem} + {deleteItem} + {readItem} + {moveItem} + {tagItem} + {rulesItem} + {copyItem} + + {starItem} + {pinItem} + {spamBlock} + {findBlock} + + {permalinkBlock} + + ) : ( + <> + {replyBlock} + {permalinkBlock} + {archiveItem} + {deleteItem} + + {moveItem} + {copyItem} + {rulesItem} + {starItem} + {pinItem} + {tagItem} + {spamBlock} + + {readItem} )} - - {/* Permalinks (#733). The conversation entry only appears when the - message actually belongs to a thread worth linking to. */} - {!showBatchActions && ( - <> - handleAction(() => { - void copyLink(buildMailPath({ mailboxId: null, emailId: email.id, threadId: null })); - })} - /> - {email.threadId && ( - handleAction(() => { - void copyLink(buildMailPath({ mailboxId: null, emailId: null, threadId: email.threadId! })); - })} - /> - )} - - - )} - - {/* Archive */} - - handleAction(showBatchActions ? onBatchArchive! : onArchive!) - } - disabled={showBatchActions ? !onBatchArchive : !onArchive} - /> - - {/* Delete */} - - handleAction(showBatchActions ? onBatchDelete! : onDelete!) - } - disabled={showBatchActions ? !onBatchDelete : !onDelete} - destructive - /> - - - - {/* Move to submenu */} - {moveTree.length > 0 && ( - - {renderMailboxNodes(moveTree, moveTargetIds, "move-to:", (mailboxId) => - showBatchActions - ? onBatchMoveToMailbox?.(mailboxId) - : onMoveToMailbox?.(mailboxId) - )} - - )} - - {/* Copy to another connected account, keeping the originals */} - {onCopyToAccount && copyTrees.length > 0 && ( - - {copyTrees.map((target) => ( -
- {target.label} - {renderMailboxNodes(target.tree, target.targetIds, `copy-to:${target.accountId}:`, (mailboxId) => - onCopyToAccount(target.accountId, mailboxId) - )} -
- ))} -
- )} - - {/* Rules: filter rules made from the sender(s), saved as Sieve */} - - - {/* Star/Unstar - only for single email */} - {!showBatchActions && ( - handleAction(onToggleStar!)} - disabled={!onToggleStar} - testId={isStarred ? "ctx-unstar" : "ctx-star"} - /> - )} - - {/* Pin/Unpin - only for single email; pinned mails float to the top of the list */} - {!showBatchActions && onTogglePinned && ( - handleAction(onTogglePinned)} - /> - )} - - {/* Set tag submenu - the row's own tags, or the whole selection's (#1077) */} - {(!showBatchActions || onBatchToggleTag) && ( - -
- {showBatchActions ? ( - onBatchToggleTag?.(tagId)} - /> - ) : ( - onSetTag?.(tagId)} - /> - )} -
-
- )} - - {/* Spam - contextual based on folder; pointless on own outgoing mail */} - {spamApplicable && ( - <> - - - - handleAction( - showBatchActions - ? (isInJunkFolder ? onBatchUndoSpam! : onBatchMarkAsSpam!) - : (isInJunkFolder ? onUndoSpam! : onMarkAsSpam!) - ) - } - disabled={showBatchActions ? (isInJunkFolder ? !onBatchUndoSpam : !onBatchMarkAsSpam) : (isInJunkFolder ? !onUndoSpam : !onMarkAsSpam)} - destructive={!isInJunkFolder} - /> - - )} - - - - {/* Mark as read/unread */} - - handleAction(() => - showBatchActions - ? onBatchMarkAsRead?.(isUnread) - : onMarkAsRead?.(isUnread) - ) - } - /> )} diff --git a/components/email/email-list.tsx b/components/email/email-list.tsx index df676ebb3..58921f677 100644 --- a/components/email/email-list.tsx +++ b/components/email/email-list.tsx @@ -38,6 +38,8 @@ interface EmailListProps { selectedEmailId?: string; onEmailSelect?: (email: Email) => void; onEmailDoubleClick?: (email: Email) => void; + /** Mountain View: search for the mail a sender has sent, from the row menu. */ + onFindFromSender?: (sender: { name: string; email: string }) => void; className?: string; isLoading?: boolean; hasMore?: boolean; @@ -75,6 +77,7 @@ export function EmailList({ selectedEmailId, onEmailSelect, onEmailDoubleClick, + onFindFromSender, className, isLoading = false, hasMore, @@ -798,6 +801,9 @@ export function EmailList({ onReplyAll={() => onReplyAll?.(contextMenuEmail!)} onForward={() => onForward?.(contextMenuEmail!)} onForwardAsAttachment={() => onForwardAsAttachment?.(contextMenuEmail!)} + onFindFromSender={onFindFromSender && contextMenuEmail.from?.[0]?.email + ? () => onFindFromSender({ name: contextMenuEmail!.from![0].name || '', email: contextMenuEmail!.from![0].email }) + : undefined} onMarkAsRead={(read) => onMarkAsRead?.(contextMenuEmail!, read)} onToggleStar={() => onToggleStar?.(contextMenuEmail!)} onTogglePinned={onTogglePinned ? () => onTogglePinned(contextMenuEmail!) : undefined} diff --git a/components/mail/mail-app.tsx b/components/mail/mail-app.tsx index bc3369c93..3502fb7ca 100644 --- a/components/mail/mail-app.tsx +++ b/components/mail/mail-app.tsx @@ -4517,6 +4517,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) { void handleSelectContactSuggestion(sender, 'from') : undefined} hideBatchToolbar={mvShell} emails={activeEmails} selectedEmailId={selectedEmail?.id} diff --git a/locales/ar/common.json b/locales/ar/common.json index 912a73b43..6af5ce394 100644 --- a/locales/ar/common.json +++ b/locales/ar/common.json @@ -2517,7 +2517,8 @@ "none": "لا توجد قواعد بعد", "new_mail_only": "للرسائل الجديدة فقط", "running": "جارٍ تشغيل «{name}» على {folder}…" - } + }, + "find_emails_from": "البحث عن رسائل من {name}" }, "mailbox_context_menu": { "mark_folder_read": "تمييز المجلد كمقروء", diff --git a/locales/ca/common.json b/locales/ca/common.json index 295d3e8db..167c4fffe 100644 --- a/locales/ca/common.json +++ b/locales/ca/common.json @@ -2517,7 +2517,8 @@ "none": "Encara no hi ha regles", "new_mail_only": "Només per a missatges nous", "running": "S'està executant «{name}» a {folder}…" - } + }, + "find_emails_from": "Cerca correus de {name}" }, "mailbox_context_menu": { "mark_folder_read": "Marca la carpeta com a llegida", diff --git a/locales/cs/common.json b/locales/cs/common.json index c7ba2469a..5a7f3a4a9 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -2517,7 +2517,8 @@ "none": "Zatím žádná pravidla", "new_mail_only": "Pouze pro nové zprávy", "running": "Spouštění „{name}“ ve složce {folder}…" - } + }, + "find_emails_from": "Najít e-maily od {name}" }, "mailbox_context_menu": { "mark_folder_read": "Označit složku jako přečtenou", diff --git a/locales/da/common.json b/locales/da/common.json index 8a2d36906..a6f2fe90d 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -2517,7 +2517,8 @@ "none": "Ingen regler endnu", "new_mail_only": "Kun for nye beskeder", "running": "Kører „{name}“ på {folder}…" - } + }, + "find_emails_from": "Find e-mails fra {name}" }, "mailbox_context_menu": { "mark_folder_read": "Markér mappe som læst", diff --git a/locales/de/common.json b/locales/de/common.json index 7e70f63b9..cf70dd9f9 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -2517,7 +2517,8 @@ "none": "Noch keine Regeln", "new_mail_only": "Nur für neue Nachrichten", "running": "„{name}“ wird auf {folder} angewendet…" - } + }, + "find_emails_from": "E-Mails von {name} finden" }, "mailbox_context_menu": { "mark_folder_read": "Ordner als gelesen markieren", diff --git a/locales/en/common.json b/locales/en/common.json index 6cb93c0ba..847e9bbe6 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -2517,7 +2517,8 @@ "none": "No rules yet", "new_mail_only": "Only for new messages", "running": "Running \"{name}\" on {folder}…" - } + }, + "find_emails_from": "Find emails from {name}" }, "mailbox_context_menu": { "mark_folder_read": "Mark folder as read", diff --git a/locales/es/common.json b/locales/es/common.json index ed8613b37..ebc031749 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -2517,7 +2517,8 @@ "none": "Aún no hay reglas", "new_mail_only": "Solo para mensajes nuevos", "running": "Ejecutando «{name}» en {folder}…" - } + }, + "find_emails_from": "Buscar correos de {name}" }, "mailbox_context_menu": { "mark_folder_read": "Marcar carpeta como leída", diff --git a/locales/fa/common.json b/locales/fa/common.json index 6988cf850..e0f9b5eaa 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -2517,7 +2517,8 @@ "none": "هنوز قانونی وجود ندارد", "new_mail_only": "فقط برای پیام‌های جدید", "running": "در حال اجرای «{name}» روی {folder}…" - } + }, + "find_emails_from": "یافتن ایمیل‌های {name}" }, "mailbox_context_menu": { "mark_folder_read": "علامت‌گذاری پوشه خوانده شده", diff --git a/locales/fr/common.json b/locales/fr/common.json index 2ae93763b..6a0c6d50f 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -2517,7 +2517,8 @@ "none": "Aucune règle pour l'instant", "new_mail_only": "Uniquement pour les nouveaux messages", "running": "Exécution de « {name} » sur {folder}…" - } + }, + "find_emails_from": "Rechercher les e-mails de {name}" }, "mailbox_context_menu": { "mark_folder_read": "Marquer le dossier comme lu", diff --git a/locales/he/common.json b/locales/he/common.json index 4855c6e77..a592937c5 100644 --- a/locales/he/common.json +++ b/locales/he/common.json @@ -2483,7 +2483,8 @@ "none": "אין עדיין כללים", "new_mail_only": "רק להודעות חדשות", "running": "מריץ את „{name}“ על {folder}…" - } + }, + "find_emails_from": "חיפוש הודעות מ{name}" }, "shortcuts": { "title": "קיצורי מקלדת", diff --git a/locales/hu/common.json b/locales/hu/common.json index d2e318587..5d835e8fc 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -2517,7 +2517,8 @@ "none": "Még nincsenek szabályok", "new_mail_only": "Csak új üzenetekre", "running": "„{name}” futtatása itt: {folder}…" - } + }, + "find_emails_from": "{name} e-mailjeinek keresése" }, "mailbox_context_menu": { "mark_folder_read": "Mappa olvasottnak jelölése", diff --git a/locales/it/common.json b/locales/it/common.json index 506d3576a..8e86db323 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -2517,7 +2517,8 @@ "none": "Nessuna regola", "new_mail_only": "Solo per i nuovi messaggi", "running": "Esecuzione di «{name}» su {folder}…" - } + }, + "find_emails_from": "Trova email da {name}" }, "mailbox_context_menu": { "mark_folder_read": "Segna cartella come letta", diff --git a/locales/ja/common.json b/locales/ja/common.json index df0360b9e..5688f4926 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -2517,7 +2517,8 @@ "none": "ルールはまだありません", "new_mail_only": "新着メッセージのみ", "running": "{folder} で「{name}」を実行中…" - } + }, + "find_emails_from": "{name} からのメールを検索" }, "mailbox_context_menu": { "mark_folder_read": "フォルダーを既読にする", diff --git a/locales/ko/common.json b/locales/ko/common.json index 76207dd9a..6d862df78 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -2517,7 +2517,8 @@ "none": "아직 규칙이 없습니다", "new_mail_only": "새 메시지에만 적용", "running": "{folder}에서 \"{name}\" 실행 중…" - } + }, + "find_emails_from": "{name}님이 보낸 이메일 찾기" }, "mailbox_context_menu": { "mark_folder_read": "폴더를 읽음으로 표시", diff --git a/locales/lv/common.json b/locales/lv/common.json index f434e214c..9400f1c12 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -2517,7 +2517,8 @@ "none": "Vēl nav kārtulu", "new_mail_only": "Tikai jaunām ziņām", "running": "Izpilda “{name}” mapē {folder}…" - } + }, + "find_emails_from": "Atrast e-pastus no {name}" }, "mailbox_context_menu": { "mark_folder_read": "Atzīmēt mapi kā lasītu", diff --git a/locales/mn/common.json b/locales/mn/common.json index 3817539eb..1732c9be4 100644 --- a/locales/mn/common.json +++ b/locales/mn/common.json @@ -2517,7 +2517,8 @@ "none": "Одоогоор дүрэм алга", "new_mail_only": "Зөвхөн шинэ мессежүүдэд", "running": "{folder} дээр \"{name}\"-г ажиллуулж байна…" - } + }, + "find_emails_from": "{name}-с ирсэн имэйлүүдийг хайх" }, "mailbox_context_menu": { "mark_folder_read": "Хавтсыг уншсан гэж тэмдэглэх", diff --git a/locales/nb/common.json b/locales/nb/common.json index ecdd00d31..ce77f582a 100644 --- a/locales/nb/common.json +++ b/locales/nb/common.json @@ -2517,7 +2517,8 @@ "none": "Ingen regler ennå", "new_mail_only": "Bare for nye meldinger", "running": "Kjører «{name}» på {folder}…" - } + }, + "find_emails_from": "Finn e-poster fra {name}" }, "mailbox_context_menu": { "mark_folder_read": "Merk mappen som lest", diff --git a/locales/nl/common.json b/locales/nl/common.json index 76ef57ce5..e8b0301f3 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -2517,7 +2517,8 @@ "none": "Nog geen regels", "new_mail_only": "Alleen voor nieuwe berichten", "running": "\"{name}\" wordt uitgevoerd op {folder}…" - } + }, + "find_emails_from": "E-mails van {name} zoeken" }, "mailbox_context_menu": { "mark_folder_read": "Map markeren als gelezen", diff --git a/locales/pl/common.json b/locales/pl/common.json index e63093fee..6e1f694b3 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -2517,7 +2517,8 @@ "none": "Brak reguł", "new_mail_only": "Tylko dla nowych wiadomości", "running": "Uruchamianie „{name}” w folderze {folder}…" - } + }, + "find_emails_from": "Znajdź e-maile od {name}" }, "mailbox_context_menu": { "mark_folder_read": "Oznacz folder jako przeczytany", diff --git a/locales/pt/common.json b/locales/pt/common.json index d3b34acf5..ff8b1bd64 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -2517,7 +2517,8 @@ "none": "Ainda não há regras", "new_mail_only": "Apenas para mensagens novas", "running": "A executar \"{name}\" em {folder}…" - } + }, + "find_emails_from": "Encontrar e-mails de {name}" }, "mailbox_context_menu": { "mark_folder_read": "Marcar pasta como lida", diff --git a/locales/ro/common.json b/locales/ro/common.json index 6c8dce968..6abf7f544 100644 --- a/locales/ro/common.json +++ b/locales/ro/common.json @@ -2517,7 +2517,8 @@ "none": "Nicio regulă încă", "new_mail_only": "Doar pentru mesajele noi", "running": "Se rulează „{name}” în {folder}…" - } + }, + "find_emails_from": "Găsește e-mailurile de la {name}" }, "mailbox_context_menu": { "mark_folder_read": "Marcați folderul ca citit", diff --git a/locales/ru/common.json b/locales/ru/common.json index 2bcbd3263..e4c19414e 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -2517,7 +2517,8 @@ "none": "Правил пока нет", "new_mail_only": "Только для новых писем", "running": "Выполняется «{name}» в папке {folder}…" - } + }, + "find_emails_from": "Найти письма от {name}" }, "mailbox_context_menu": { "mark_folder_read": "Отметить папку как прочитанную", diff --git a/locales/sk/common.json b/locales/sk/common.json index a16753678..2aa631e8d 100644 --- a/locales/sk/common.json +++ b/locales/sk/common.json @@ -2517,7 +2517,8 @@ "none": "Zatiaľ žiadne pravidlá", "new_mail_only": "Len pre nové správy", "running": "Spúšťa sa „{name}“ v priečinku {folder}…" - } + }, + "find_emails_from": "Nájsť e-maily od {name}" }, "mailbox_context_menu": { "mark_folder_read": "Označiť priečinok ako prečítaný", diff --git a/locales/tr/common.json b/locales/tr/common.json index f65be8d1d..c7b078df1 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -2517,7 +2517,8 @@ "none": "Henüz kural yok", "new_mail_only": "Yalnızca yeni iletiler için", "running": "\"{name}\" {folder} klasöründe çalıştırılıyor…" - } + }, + "find_emails_from": "{name} adlı kişiden gelen e-postaları bul" }, "mailbox_context_menu": { "mark_folder_read": "Klasörü okundu olarak işaretle", diff --git a/locales/uk/common.json b/locales/uk/common.json index 00948d82d..a6998c3f8 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -2517,7 +2517,8 @@ "none": "Правил поки немає", "new_mail_only": "Лише для нових листів", "running": "Виконується «{name}» у теці {folder}…" - } + }, + "find_emails_from": "Знайти листи від {name}" }, "mailbox_context_menu": { "mark_folder_read": "Позначити папку як прочитану", diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index 80c517d98..804939a41 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -2517,7 +2517,8 @@ "none": "尚無規則", "new_mail_only": "僅限新郵件", "running": "正在 {folder} 中執行「{name}」…" - } + }, + "find_emails_from": "尋找來自 {name} 的郵件" }, "mailbox_context_menu": { "mark_folder_read": "將信件匣標示為已讀", diff --git a/locales/zh/common.json b/locales/zh/common.json index 2a1c7a15a..9913e1bb0 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -2517,7 +2517,8 @@ "none": "暂无规则", "new_mail_only": "仅限新邮件", "running": "正在 {folder} 中运行“{name}”…" - } + }, + "find_emails_from": "查找来自 {name} 的邮件" }, "mailbox_context_menu": { "mark_folder_read": "将文件夹标记为已读",