From c2f80e2d29ce86269943df066c20ea02ce7f8963 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Wed, 4 Nov 2026 11:42:22 +0100 Subject: [PATCH] fix: count every account's unread mail in the rail's Mail badge and the tab title, as the favicon does --- components/favicon-badge.tsx | 29 ++------------ .../navigation-rail-badge-name.test.tsx | 26 +++++++++++++ components/layout/navigation-rail.tsx | 7 ++-- components/mail/mail-app.tsx | 15 ++++++-- hooks/use-all-inboxes-unread.ts | 38 +++++++++++++++++++ lib/__tests__/tab-title.test.ts | 24 +++++++++++- lib/tab-title.ts | 22 +++++++++++ locales/ar/common.json | 4 +- locales/ca/common.json | 4 +- locales/cs/common.json | 4 +- locales/da/common.json | 4 +- locales/de/common.json | 4 +- locales/en/common.json | 4 +- locales/es/common.json | 4 +- locales/et/common.json | 4 +- locales/fa/common.json | 4 +- locales/fr/common.json | 4 +- locales/he/common.json | 4 +- locales/hu/common.json | 4 +- locales/it/common.json | 4 +- locales/ja/common.json | 4 +- locales/ko/common.json | 4 +- locales/lv/common.json | 4 +- locales/mn/common.json | 4 +- locales/nb/common.json | 4 +- locales/nl/common.json | 4 +- locales/pl/common.json | 4 +- locales/pt/common.json | 4 +- locales/ro/common.json | 4 +- locales/ru/common.json | 4 +- locales/sk/common.json | 4 +- locales/th/common.json | 4 +- locales/tr/common.json | 4 +- locales/uk/common.json | 4 +- locales/vi/common.json | 4 +- locales/zh-TW/common.json | 4 +- locales/zh/common.json | 4 +- 37 files changed, 217 insertions(+), 64 deletions(-) create mode 100644 hooks/use-all-inboxes-unread.ts diff --git a/components/favicon-badge.tsx b/components/favicon-badge.tsx index 1bc199925..a961d0f29 100644 --- a/components/favicon-badge.tsx +++ b/components/favicon-badge.tsx @@ -1,13 +1,9 @@ "use client"; -import { useEmailStore } from "@/stores/email-store"; import { useSettingsStore } from "@/stores/settings-store"; import { useFaviconBadge } from "@/hooks/use-favicon-badge"; import { useAppBadge } from "@/hooks/use-app-badge"; -import { useShallow } from "zustand/react/shallow"; -import { useAccountStore } from "@/stores/account-store"; -import { useAuthStore } from "@/stores/auth-store"; -import { totalInboxUnread } from "@/lib/account-inbox-unread"; +import { useAllInboxesUnread } from "@/hooks/use-all-inboxes-unread"; /** * Badges the browser-tab favicon, and — where supported — the installed PWA's @@ -31,33 +27,14 @@ import { totalInboxUnread } from "@/lib/account-inbox-unread"; * Renders nothing. */ export function FaviconBadge() { - // The store's canonical inbox selector. `role === 'inbox'` alone is not - // enough: shared and group inboxes ship in the same `mailboxes` array, so on - // a delegated setup the first match can be somebody else's inbox. - const inboxUnread = useEmailStore( - (s) => s.mailboxes.find((m) => m.role === "inbox" && !m.isShared)?.unreadEmails ?? 0, - ); const enabled = useSettingsStore((s) => s.faviconUnreadBadge); - const allAccounts = useSettingsStore((s) => s.unreadBadgeAllAccounts); // With several accounts the badge counts the inbox of each (on by default): // new mail in an account that is not on screen still shows on the icon and - // in the desktop tray. The other accounts' folder lists are what the - // account switcher reads (useProMultiAccountMailboxes); one not loaded yet - // adds nothing. - const othersUnread = useOtherAccountsUnread(allAccounts); - const count = inboxUnread + othersUnread; + // in the desktop tray. + const count = useAllInboxesUnread(); useFaviconBadge(count, enabled); useAppBadge(count, enabled); return null; } - -function useOtherAccountsUnread(enabled: boolean): number { - const activeAccountId = useAuthStore((s) => s.activeAccountId); - const otherIds = useAccountStore( - useShallow((s) => (enabled ? s.accounts.filter((a) => a.isConnected && a.id !== activeAccountId).map((a) => a.id) : [])), - ); - const accountMailboxes = useEmailStore((s) => s.accountMailboxes); - return totalInboxUnread(otherIds, accountMailboxes); -} diff --git a/components/layout/__tests__/navigation-rail-badge-name.test.tsx b/components/layout/__tests__/navigation-rail-badge-name.test.tsx index c3baba3de..bf3d0aa5b 100644 --- a/components/layout/__tests__/navigation-rail-badge-name.test.tsx +++ b/components/layout/__tests__/navigation-rail-badge-name.test.tsx @@ -63,3 +63,29 @@ describe.each([ expect(link.textContent).not.toContain('2'); }); }); + +// Usability test 2026-11-04, mail-6: with two accounts the rail said 10 while +// 30 were unread in all; the favicon already counted every account. +describe('Mail entry with several accounts', () => { + const twoAccounts = async () => { + const { useAccountStore } = await import('@/stores/account-store'); + const { useAuthStore } = await import('@/stores/auth-store'); + useAccountStore.setState({ accounts: [{ id: 'acc-a', isConnected: true }, { id: 'acc-b', isConnected: true }] } as never); + useAuthStore.setState({ activeAccountId: 'acc-a' } as never); + useEmailStore.setState({ accountMailboxes: { 'acc-b': [{ id: 'b-in', role: 'inbox', unreadEmails: 5 }] } } as never); + }; + + it('counts the other accounts\' inboxes too', async () => { + await twoAccounts(); + useSettingsStore.setState({ unreadBadgeAllAccounts: true }); + render(); + expect(screen.getAllByRole('link', { name: 'mail, 7 unread' }).length).toBeGreaterThan(0); + }); + + it('counts the open account only when Notifications says so', async () => { + await twoAccounts(); + useSettingsStore.setState({ unreadBadgeAllAccounts: false }); + render(); + expect(screen.getAllByRole('link', { name: 'mail, 2 unread' }).length).toBeGreaterThan(0); + }); +}); diff --git a/components/layout/navigation-rail.tsx b/components/layout/navigation-rail.tsx index c123da7f6..e36d84345 100644 --- a/components/layout/navigation-rail.tsx +++ b/components/layout/navigation-rail.tsx @@ -12,7 +12,6 @@ import { useShallow } from "zustand/react/shallow"; import { usePathname, Link, useRouter } from "@/i18n/navigation"; import { useTranslations } from "next-intl"; import { useCalendarStore } from "@/stores/calendar-store"; -import { useEmailStore } from "@/stores/email-store"; import { useSettingsStore } from "@/stores/settings-store"; import { usePolicyStore } from "@/stores/policy-store"; import { useResolvedSidebarApps } from "@/hooks/use-resolved-sidebar-apps"; @@ -34,6 +33,7 @@ import { toUnicodeEmail } from "@/lib/idn"; import { usePluginPages, pluginPageHref, isPluginPageActive } from "@/hooks/use-plugin-pages"; import { NotificationBell } from "@/components/notifications/notification-center"; import { useBottomControls } from "@/hooks/use-bottom-controls"; +import { useAllInboxesUnread } from "@/hooks/use-all-inboxes-unread"; import { writeRailHints, type RailHint } from "@/lib/boot-skeleton/hints"; interface NavItem { @@ -260,7 +260,6 @@ export function NavigationRail({ const { appLogoLightUrl, appLogoDarkUrl } = useConfig(); const resolvedTheme = useThemeStore((s) => s.resolvedTheme); const { supportsCalendar } = useCalendarStore(useShallow((state) => ({ supportsCalendar: state.supportsCalendar }))); - const { mailboxes } = useEmailStore(useShallow((state) => ({ mailboxes: state.mailboxes }))); const client = useAuthStore((s) => s.client); const supportsFiles = client?.supportsFiles() ?? false; const supportsContacts = client?.supportsContacts() ?? false; @@ -277,7 +276,9 @@ export function NavigationRail({ const visibleSidebarApps = useResolvedSidebarApps(); // Pages plugins added (api.ui.setPages); listed after the built-in apps. const pluginPages = usePluginPages(); - const inboxUnread = mailboxes.find(m => m.role === "inbox")?.unreadEmails || 0; + // Every signed-in account's inbox, as the favicon and the tab title count + // it (mail-6), unless Notifications says to count the open one only. + const inboxUnread = useAllInboxesUnread(); const isStalwartAdmin = useStalwartAdmin(); const hasUpdate = useUpdateStore(selectHasUpdate); const updateSeverity = useUpdateStore((s) => s.status?.severity); diff --git a/components/mail/mail-app.tsx b/components/mail/mail-app.tsx index 560bf805f..6c18797aa 100644 --- a/components/mail/mail-app.tsx +++ b/components/mail/mail-app.tsx @@ -152,7 +152,8 @@ import { ResizeHandle } from "@/components/layout/resize-handle"; import { Button } from "@/components/ui/button"; import { ToolbarButton, ToolbarSearchAction } from "@/components/ui/toolbar"; import { useDocumentTitle } from "@/hooks/use-document-title"; -import { mailTitleContext } from "@/lib/tab-title"; +import { mailTitleContext, mailboxTitleLine } from "@/lib/tab-title"; +import { useOtherAccountsUnread } from "@/hooks/use-all-inboxes-unread"; import { usePluginStore } from "@/stores/plugin-store"; import { AppTopBannerSlot } from "@/components/plugins/app-top-banner-slot"; import { canDownload } from "@/lib/device-policy"; @@ -1472,6 +1473,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) { // The subject is kept out until the policy has loaded: a gate that keeps // subjects out of browser history must not let one through while unknown. const subjectInTabTitle = usePolicyStore((s) => s.loaded && s.isFeatureEnabled('tabTitleSubjectEnabled')); + const otherAccountsUnread = useOtherAccountsUnread(); const titleContext = useMemo(() => { // Composing email const composer = showComposer @@ -1491,12 +1493,17 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) { const mailbox = selectedMailbox ? mailboxes.find(mb => mb.id === selectedMailbox) : undefined; if (mailbox) { const mailboxName = localizeMailboxName(mailbox.role, mailbox.name, (k) => t(`sidebar.mailboxes.${k}`)); - const unreadCount = mailbox.unreadEmails || 0; - mailboxLine = unreadCount > 0 ? `${mailboxName} (${unreadCount})` : mailboxName; + // The open account's own Inbox also counts the other accounts' (mail-6). + mailboxLine = mailboxTitleLine({ + name: mailboxName, + unread: mailbox.unreadEmails || 0, + otherUnread: otherAccountsUnread, + ownInbox: mailbox.role === 'inbox' && !mailbox.isShared && !isUnifiedView, + }, t); } return mailTitleContext({ composer, subject, mailbox: mailboxLine }, { showSubject: subjectInTabTitle }); - }, [showComposer, composerMode, selectedEmail, selectedMailbox, mailboxes, t, subjectInTabTitle]); + }, [showComposer, composerMode, selectedEmail, selectedMailbox, mailboxes, t, subjectInTabTitle, otherAccountsUnread, isUnifiedView]); useDocumentTitle(titleContext); // When this page is rendered inside the Pro shell as the Mail tab body, diff --git a/hooks/use-all-inboxes-unread.ts b/hooks/use-all-inboxes-unread.ts new file mode 100644 index 000000000..bf3e04553 --- /dev/null +++ b/hooks/use-all-inboxes-unread.ts @@ -0,0 +1,38 @@ +"use client"; + +import { useShallow } from "zustand/react/shallow"; +import { useEmailStore } from "@/stores/email-store"; +import { useSettingsStore } from "@/stores/settings-store"; +import { useAccountStore } from "@/stores/account-store"; +import { useAuthStore } from "@/stores/auth-store"; +import { totalInboxUnread } from "@/lib/account-inbox-unread"; + +/** + * Unread mail in the inboxes of the signed-in accounts that aren't on + * screen, while "count every account" is on in Notifications (the default). + * The other accounts' folder lists are what the account switcher reads + * (useProMultiAccountMailboxes); one not loaded yet adds nothing. + */ +export function useOtherAccountsUnread(): number { + const enabled = useSettingsStore((s) => s.unreadBadgeAllAccounts); + const activeAccountId = useAuthStore((s) => s.activeAccountId); + const otherIds = useAccountStore( + useShallow((s) => (enabled ? s.accounts.filter((a) => a.isConnected && a.id !== activeAccountId).map((a) => a.id) : [])), + ); + const accountMailboxes = useEmailStore((s) => s.accountMailboxes); + return totalInboxUnread(otherIds, accountMailboxes); +} + +/** + * The number the app shows for new mail: the open account's own inbox plus, + * by the same setting, every other account's. The favicon, the app badge, + * the rail's Mail entry and the tab title all show this one number + * (usability test 2026-11-04, mail-6). + */ +export function useAllInboxesUnread(): number { + // `role === 'inbox'` alone is not enough: shared and group inboxes ship in + // the same `mailboxes` array, so on a delegated setup the first match can + // be somebody else's inbox. + const own = useEmailStore((s) => s.mailboxes.find((m) => m.role === "inbox" && !m.isShared)?.unreadEmails ?? 0); + return own + useOtherAccountsUnread(); +} diff --git a/lib/__tests__/tab-title.test.ts b/lib/__tests__/tab-title.test.ts index 1b0073fe8..f375b2890 100644 --- a/lib/__tests__/tab-title.test.ts +++ b/lib/__tests__/tab-title.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { formatTabTitle, mailTitleContext } from '../tab-title'; +import { formatTabTitle, mailTitleContext, mailboxTitleLine } from '../tab-title'; describe('formatTabTitle', () => { it('puts the context first, the account next and the app name last', () => { @@ -50,3 +50,25 @@ describe('mailTitleContext', () => { expect(mailTitleContext(view, { showSubject: false })).toBe('Reply'); }); }); + +describe('mailboxTitleLine', () => { + const t = (key: string, values?: Record) => `${key} ${JSON.stringify(values)}`; + + it('names the folder with its unread count', () => { + expect(mailboxTitleLine({ name: 'Family', unread: 2 }, t)).toBe('Family (2)'); + expect(mailboxTitleLine({ name: 'Family', unread: 0 }, t)).toBe('Family'); + }); + + // mail-6: the title said 10 while 30 were unread in all accounts. + it('adds the other accounts on the Inbox', () => { + expect(mailboxTitleLine({ name: 'Inbox', unread: 10, otherUnread: 20, ownInbox: true }, t)) + .toBe('mail_p2_folders.title_all_accounts {"name":"Inbox","count":10,"total":30}'); + expect(mailboxTitleLine({ name: 'Inbox', unread: 0, otherUnread: 20, ownInbox: true }, t)) + .toBe('mail_p2_folders.title_all_accounts_only {"name":"Inbox","total":20}'); + }); + + it('leaves other folders and an Inbox with no other unread mail alone', () => { + expect(mailboxTitleLine({ name: 'Family', unread: 1, otherUnread: 20 }, t)).toBe('Family (1)'); + expect(mailboxTitleLine({ name: 'Inbox', unread: 10, otherUnread: 0, ownInbox: true }, t)).toBe('Inbox (10)'); + }); +}); diff --git a/lib/tab-title.ts b/lib/tab-title.ts index 87fa1ce2f..503e28918 100644 --- a/lib/tab-title.ts +++ b/lib/tab-title.ts @@ -34,3 +34,25 @@ export function mailTitleContext( ): string | null { return view.composer || (showSubject ? view.subject : null) || view.mailbox || null; } + +type Translate = (key: string, values?: Record) => string; + +/** + * The mailbox line of the tab title: "Inbox (10)", or for the open account's + * own Inbox while other accounts have unread mail too, "Inbox (10, 30 in all + * accounts)", the number the favicon and the rail show (usability test + * 2026-11-04, mail-6). `otherUnread` is 0 when Notifications counts the open + * account only. + */ +export function mailboxTitleLine( + { name, unread, otherUnread = 0, ownInbox = false }: { name: string; unread: number; otherUnread?: number; ownInbox?: boolean }, + t: Translate, +): string { + if (ownInbox && otherUnread > 0) { + const total = unread + otherUnread; + return unread > 0 + ? t('mail_p2_folders.title_all_accounts', { name, count: unread, total }) + : t('mail_p2_folders.title_all_accounts_only', { name, total }); + } + return unread > 0 ? `${name} (${unread})` : name; +} diff --git a/locales/ar/common.json b/locales/ar/common.json index b3bc36ab5..59634352f 100644 --- a/locales/ar/common.json +++ b/locales/ar/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "إفراغ المهملات", "empty_spam": "إفراغ البريد العشوائي", "empty_trash_confirm": "حذف {count, plural, zero {الرسائل} one {الرسالة} two {الرسالتين} few {كل الرسائل الـ#} many {كل الرسائل الـ#} other {كل الرسائل الـ#}} في المهملات نهائيًا؟ لا يمكن التراجع عن ذلك.", - "empty_spam_confirm": "حذف {count, plural, zero {الرسائل} one {الرسالة} two {الرسالتين} few {كل الرسائل الـ#} many {كل الرسائل الـ#} other {كل الرسائل الـ#}} في البريد العشوائي نهائيًا؟ لا يمكن التراجع عن ذلك." + "empty_spam_confirm": "حذف {count, plural, zero {الرسائل} one {الرسالة} two {الرسالتين} few {كل الرسائل الـ#} many {كل الرسائل الـ#} other {كل الرسائل الـ#}} في البريد العشوائي نهائيًا؟ لا يمكن التراجع عن ذلك.", + "title_all_accounts": "{name} ({count}، {total} في كل الحسابات)", + "title_all_accounts_only": "{name} ({total} في كل الحسابات)" }, "uxs_folders": { "manage_accounts": "إدارة الحسابات…", diff --git a/locales/ca/common.json b/locales/ca/common.json index 370ca65ba..2336353f6 100644 --- a/locales/ca/common.json +++ b/locales/ca/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Buida la paperera", "empty_spam": "Buida el correu brossa", "empty_trash_confirm": "Vols suprimir per sempre {count, plural, one {el missatge} other {els # missatges}} de la paperera? No es pot desfer.", - "empty_spam_confirm": "Vols suprimir per sempre {count, plural, one {el missatge} other {els # missatges}} del correu brossa? No es pot desfer." + "empty_spam_confirm": "Vols suprimir per sempre {count, plural, one {el missatge} other {els # missatges}} del correu brossa? No es pot desfer.", + "title_all_accounts": "{name} ({count}, {total} a tots els comptes)", + "title_all_accounts_only": "{name} ({total} a tots els comptes)" }, "uxs_folders": { "manage_accounts": "Gestiona els comptes…", diff --git a/locales/cs/common.json b/locales/cs/common.json index 143e0cdcf..a4f189c06 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Vysypat koš", "empty_spam": "Vyprázdnit spam", "empty_trash_confirm": "Trvale smazat {count, plural, one {zprávu} few {všechny # zprávy} many {všech # zprávy} other {všech # zpráv}} v koši? Nelze to vrátit zpět.", - "empty_spam_confirm": "Trvale smazat {count, plural, one {zprávu} few {všechny # zprávy} many {všech # zprávy} other {všech # zpráv}} ve spamu? Nelze to vrátit zpět." + "empty_spam_confirm": "Trvale smazat {count, plural, one {zprávu} few {všechny # zprávy} many {všech # zprávy} other {všech # zpráv}} ve spamu? Nelze to vrátit zpět.", + "title_all_accounts": "{name} ({count}, {total} ve všech účtech)", + "title_all_accounts_only": "{name} ({total} ve všech účtech)" }, "uxs_folders": { "manage_accounts": "Spravovat účty…", diff --git a/locales/da/common.json b/locales/da/common.json index d03c97b2d..5bfef5971 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Tøm papirkurven", "empty_spam": "Tøm spam", "empty_trash_confirm": "Slet {count, plural, one {beskeden} other {alle # beskeder}} i papirkurven for altid? Det kan ikke fortrydes.", - "empty_spam_confirm": "Slet {count, plural, one {beskeden} other {alle # beskeder}} i spam for altid? Det kan ikke fortrydes." + "empty_spam_confirm": "Slet {count, plural, one {beskeden} other {alle # beskeder}} i spam for altid? Det kan ikke fortrydes.", + "title_all_accounts": "{name} ({count}, {total} i alle konti)", + "title_all_accounts_only": "{name} ({total} i alle konti)" }, "uxs_folders": { "manage_accounts": "Administrer konti…", diff --git a/locales/de/common.json b/locales/de/common.json index c3535957e..a00c23fe5 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Papierkorb leeren", "empty_spam": "Spam leeren", "empty_trash_confirm": "{count, plural, one {Die Nachricht} other {Alle # Nachrichten}} im Papierkorb endgültig löschen? Das lässt sich nicht rückgängig machen.", - "empty_spam_confirm": "{count, plural, one {Die Nachricht} other {Alle # Nachrichten}} im Spam endgültig löschen? Das lässt sich nicht rückgängig machen." + "empty_spam_confirm": "{count, plural, one {Die Nachricht} other {Alle # Nachrichten}} im Spam endgültig löschen? Das lässt sich nicht rückgängig machen.", + "title_all_accounts": "{name} ({count}, {total} in allen Konten)", + "title_all_accounts_only": "{name} ({total} in allen Konten)" }, "uxs_folders": { "manage_accounts": "Konten verwalten…", diff --git a/locales/en/common.json b/locales/en/common.json index 045019132..972b3cb69 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Empty Trash", "empty_spam": "Empty Spam", "empty_trash_confirm": "Delete {count, plural, one {the message} other {all # messages}} in Trash forever? You can't undo this.", - "empty_spam_confirm": "Delete {count, plural, one {the message} other {all # messages}} in Spam forever? You can't undo this." + "empty_spam_confirm": "Delete {count, plural, one {the message} other {all # messages}} in Spam forever? You can't undo this.", + "title_all_accounts": "{name} ({count}, {total} in all accounts)", + "title_all_accounts_only": "{name} ({total} in all accounts)" }, "uxs_folders": { "manage_accounts": "Manage accounts…", diff --git a/locales/es/common.json b/locales/es/common.json index 813d5b46c..90d265dcb 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Vaciar papelera", "empty_spam": "Vaciar spam", "empty_trash_confirm": "¿Eliminar para siempre {count, plural, one {el mensaje} other {los # mensajes}} de la papelera? No se puede deshacer.", - "empty_spam_confirm": "¿Eliminar para siempre {count, plural, one {el mensaje} other {los # mensajes}} de spam? No se puede deshacer." + "empty_spam_confirm": "¿Eliminar para siempre {count, plural, one {el mensaje} other {los # mensajes}} de spam? No se puede deshacer.", + "title_all_accounts": "{name} ({count}, {total} en todas las cuentas)", + "title_all_accounts_only": "{name} ({total} en todas las cuentas)" }, "uxs_folders": { "manage_accounts": "Gestionar cuentas…", diff --git a/locales/et/common.json b/locales/et/common.json index bb07164c9..f21bc157e 100644 --- a/locales/et/common.json +++ b/locales/et/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Tühjenda prügikast", "empty_spam": "Tühjenda rämpspost", "empty_trash_confirm": "Kas kustutada prügikastist {count, plural, one {kiri} other {kõik # kirja}} jäädavalt? Seda ei saa tagasi võtta.", - "empty_spam_confirm": "Kas kustutada rämpspostist {count, plural, one {kiri} other {kõik # kirja}} jäädavalt? Seda ei saa tagasi võtta." + "empty_spam_confirm": "Kas kustutada rämpspostist {count, plural, one {kiri} other {kõik # kirja}} jäädavalt? Seda ei saa tagasi võtta.", + "title_all_accounts": "{name} ({count}, {total} kõigil kontodel)", + "title_all_accounts_only": "{name} ({total} kõigil kontodel)" }, "uxs_folders": { "manage_accounts": "Halda kontosid…", diff --git a/locales/fa/common.json b/locales/fa/common.json index 33a32e3dc..64ed75bba 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "خالی کردن زباله‌دان", "empty_spam": "خالی کردن هرزنامه", "empty_trash_confirm": "{count, plural, one {پیام} other {همه # پیام}} زباله‌دان برای همیشه حذف شود؟ این کار برگشت‌پذیر نیست.", - "empty_spam_confirm": "{count, plural, one {پیام} other {همه # پیام}} هرزنامه برای همیشه حذف شود؟ این کار برگشت‌پذیر نیست." + "empty_spam_confirm": "{count, plural, one {پیام} other {همه # پیام}} هرزنامه برای همیشه حذف شود؟ این کار برگشت‌پذیر نیست.", + "title_all_accounts": "{name} ({count}، {total} در همه حساب‌ها)", + "title_all_accounts_only": "{name} ({total} در همه حساب‌ها)" }, "uxs_folders": { "manage_accounts": "مدیریت حساب‌ها…", diff --git a/locales/fr/common.json b/locales/fr/common.json index 916bc5ca5..86bc4dbf7 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Vider la corbeille", "empty_spam": "Vider le spam", "empty_trash_confirm": "Supprimer définitivement {count, plural, one {le message} other {les # messages}} de la corbeille ? Impossible de revenir en arrière.", - "empty_spam_confirm": "Supprimer définitivement {count, plural, one {le message} other {les # messages}} du spam ? Impossible de revenir en arrière." + "empty_spam_confirm": "Supprimer définitivement {count, plural, one {le message} other {les # messages}} du spam ? Impossible de revenir en arrière.", + "title_all_accounts": "{name} ({count}, {total} dans tous les comptes)", + "title_all_accounts_only": "{name} ({total} dans tous les comptes)" }, "uxs_folders": { "manage_accounts": "Gérer les comptes…", diff --git a/locales/he/common.json b/locales/he/common.json index fa675fa12..5c518d00b 100644 --- a/locales/he/common.json +++ b/locales/he/common.json @@ -5544,7 +5544,9 @@ "empty_trash": "ריקון האשפה", "empty_spam": "ריקון הספאם", "empty_trash_confirm": "למחוק לצמיתות {count, plural, one {את ההודעה} two {את שתי ההודעות} other {את כל # ההודעות}} באשפה? אי אפשר לבטל את זה.", - "empty_spam_confirm": "למחוק לצמיתות {count, plural, one {את ההודעה} two {את שתי ההודעות} other {את כל # ההודעות}} בספאם? אי אפשר לבטל את זה." + "empty_spam_confirm": "למחוק לצמיתות {count, plural, one {את ההודעה} two {את שתי ההודעות} other {את כל # ההודעות}} בספאם? אי אפשר לבטל את זה.", + "title_all_accounts": "{name} ({count}, {total} בכל החשבונות)", + "title_all_accounts_only": "{name} ({total} בכל החשבונות)" }, "uxs_folders": { "manage_accounts": "ניהול חשבונות…", diff --git a/locales/hu/common.json b/locales/hu/common.json index f8e6273cd..ab9f51a29 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Kuka ürítése", "empty_spam": "Spam ürítése", "empty_trash_confirm": "Véglegesen törli {count, plural, one {az üzenetet} other {mind a # üzenetet}} a kukából? Ez nem vonható vissza.", - "empty_spam_confirm": "Véglegesen törli {count, plural, one {az üzenetet} other {mind a # üzenetet}} a spamből? Ez nem vonható vissza." + "empty_spam_confirm": "Véglegesen törli {count, plural, one {az üzenetet} other {mind a # üzenetet}} a spamből? Ez nem vonható vissza.", + "title_all_accounts": "{name} ({count}, {total} az összes fiókban)", + "title_all_accounts_only": "{name} ({total} az összes fiókban)" }, "uxs_folders": { "manage_accounts": "Fiókok kezelése…", diff --git a/locales/it/common.json b/locales/it/common.json index 148745ae2..e1e8b2a4f 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Svuota cestino", "empty_spam": "Svuota spam", "empty_trash_confirm": "Eliminare per sempre {count, plural, one {il messaggio} other {tutti i # messaggi}} nel cestino? Non si può annullare.", - "empty_spam_confirm": "Eliminare per sempre {count, plural, one {il messaggio} other {tutti i # messaggi}} nello spam? Non si può annullare." + "empty_spam_confirm": "Eliminare per sempre {count, plural, one {il messaggio} other {tutti i # messaggi}} nello spam? Non si può annullare.", + "title_all_accounts": "{name} ({count}, {total} in tutti gli account)", + "title_all_accounts_only": "{name} ({total} in tutti gli account)" }, "uxs_folders": { "manage_accounts": "Gestisci account…", diff --git a/locales/ja/common.json b/locales/ja/common.json index db0613b81..6e14746ca 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "ゴミ箱を空にする", "empty_spam": "迷惑メールを空にする", "empty_trash_confirm": "ゴミ箱の{count, plural, other {# 件のメッセージ}}をすべて完全に削除しますか?元に戻せません。", - "empty_spam_confirm": "迷惑メールの{count, plural, other {# 件のメッセージ}}をすべて完全に削除しますか?元に戻せません。" + "empty_spam_confirm": "迷惑メールの{count, plural, other {# 件のメッセージ}}をすべて完全に削除しますか?元に戻せません。", + "title_all_accounts": "{name} ({count}、すべてのアカウントで {total})", + "title_all_accounts_only": "{name} (すべてのアカウントで {total})" }, "uxs_folders": { "manage_accounts": "アカウントを管理…", diff --git a/locales/ko/common.json b/locales/ko/common.json index ee9794db8..c2bc54f27 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "휴지통 비우기", "empty_spam": "스팸 비우기", "empty_trash_confirm": "휴지통의 {count, plural, other {메시지 #개}}를 모두 영구 삭제할까요? 되돌릴 수 없어요.", - "empty_spam_confirm": "스팸의 {count, plural, other {메시지 #개}}를 모두 영구 삭제할까요? 되돌릴 수 없어요." + "empty_spam_confirm": "스팸의 {count, plural, other {메시지 #개}}를 모두 영구 삭제할까요? 되돌릴 수 없어요.", + "title_all_accounts": "{name} ({count}, 모든 계정에 {total})", + "title_all_accounts_only": "{name} (모든 계정에 {total})" }, "uxs_folders": { "manage_accounts": "계정 관리…", diff --git a/locales/lv/common.json b/locales/lv/common.json index 5550d87a6..7b9e46c07 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Iztukšot miskasti", "empty_spam": "Iztukšot mēstules", "empty_trash_confirm": "Neatgriezeniski dzēst {count, plural, =1 {vēstuli} zero {visas # vēstules} one {visas # vēstuli} other {visas # vēstules}} miskastē? To nevar atsaukt.", - "empty_spam_confirm": "Neatgriezeniski dzēst {count, plural, =1 {vēstuli} zero {visas # vēstules} one {visas # vēstuli} other {visas # vēstules}} mēstulēs? To nevar atsaukt." + "empty_spam_confirm": "Neatgriezeniski dzēst {count, plural, =1 {vēstuli} zero {visas # vēstules} one {visas # vēstuli} other {visas # vēstules}} mēstulēs? To nevar atsaukt.", + "title_all_accounts": "{name} ({count}, {total} visos kontos)", + "title_all_accounts_only": "{name} ({total} visos kontos)" }, "uxs_folders": { "manage_accounts": "Pārvaldīt kontus…", diff --git a/locales/mn/common.json b/locales/mn/common.json index 9c3ff1cb0..10389dae5 100644 --- a/locales/mn/common.json +++ b/locales/mn/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Хогийн савыг хоослох", "empty_spam": "Спамыг хоослох", "empty_trash_confirm": "Хогийн саван дахь {count, plural, one {захидлыг} other {бүх # захидлыг}} бүрмөсөн устгах уу? Буцаах боломжгүй.", - "empty_spam_confirm": "Спам дахь {count, plural, one {захидлыг} other {бүх # захидлыг}} бүрмөсөн устгах уу? Буцаах боломжгүй." + "empty_spam_confirm": "Спам дахь {count, plural, one {захидлыг} other {бүх # захидлыг}} бүрмөсөн устгах уу? Буцаах боломжгүй.", + "title_all_accounts": "{name} ({count}, бүх бүртгэлд {total})", + "title_all_accounts_only": "{name} (бүх бүртгэлд {total})" }, "uxs_folders": { "manage_accounts": "Данснуудыг удирдах…", diff --git a/locales/nb/common.json b/locales/nb/common.json index dea326255..6b64d5f83 100644 --- a/locales/nb/common.json +++ b/locales/nb/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Tøm papirkurven", "empty_spam": "Tøm søppelpost", "empty_trash_confirm": "Slette {count, plural, one {meldingen} other {alle # meldingene}} i papirkurven for godt? Dette kan ikke angres.", - "empty_spam_confirm": "Slette {count, plural, one {meldingen} other {alle # meldingene}} i søppelpost for godt? Dette kan ikke angres." + "empty_spam_confirm": "Slette {count, plural, one {meldingen} other {alle # meldingene}} i søppelpost for godt? Dette kan ikke angres.", + "title_all_accounts": "{name} ({count}, {total} i alle kontoer)", + "title_all_accounts_only": "{name} ({total} i alle kontoer)" }, "uxs_folders": { "manage_accounts": "Administrer kontoer…", diff --git a/locales/nl/common.json b/locales/nl/common.json index 38065b464..97a29b7d0 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Prullenbak legen", "empty_spam": "Spam legen", "empty_trash_confirm": "{count, plural, one {Het bericht} other {Alle # berichten}} in de prullenbak definitief verwijderen? Dit kan niet ongedaan worden.", - "empty_spam_confirm": "{count, plural, one {Het bericht} other {Alle # berichten}} in spam definitief verwijderen? Dit kan niet ongedaan worden." + "empty_spam_confirm": "{count, plural, one {Het bericht} other {Alle # berichten}} in spam definitief verwijderen? Dit kan niet ongedaan worden.", + "title_all_accounts": "{name} ({count}, {total} in alle accounts)", + "title_all_accounts_only": "{name} ({total} in alle accounts)" }, "uxs_folders": { "manage_accounts": "Accounts beheren…", diff --git a/locales/pl/common.json b/locales/pl/common.json index 7c5252bcd..69d42e56b 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Opróżnij kosz", "empty_spam": "Opróżnij spam", "empty_trash_confirm": "Trwale usunąć {count, plural, one {wiadomość} few {wszystkie # wiadomości} many {wszystkie # wiadomości} other {wszystkie # wiadomości}} z kosza? Nie można tego cofnąć.", - "empty_spam_confirm": "Trwale usunąć {count, plural, one {wiadomość} few {wszystkie # wiadomości} many {wszystkie # wiadomości} other {wszystkie # wiadomości}} ze spamu? Nie można tego cofnąć." + "empty_spam_confirm": "Trwale usunąć {count, plural, one {wiadomość} few {wszystkie # wiadomości} many {wszystkie # wiadomości} other {wszystkie # wiadomości}} ze spamu? Nie można tego cofnąć.", + "title_all_accounts": "{name} ({count}, {total} we wszystkich kontach)", + "title_all_accounts_only": "{name} ({total} we wszystkich kontach)" }, "uxs_folders": { "manage_accounts": "Zarządzaj kontami…", diff --git a/locales/pt/common.json b/locales/pt/common.json index 8703862ee..b2186f497 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Esvaziar lixeira", "empty_spam": "Esvaziar spam", "empty_trash_confirm": "Excluir para sempre {count, plural, one {a mensagem} other {as # mensagens}} da lixeira? Não é possível desfazer.", - "empty_spam_confirm": "Excluir para sempre {count, plural, one {a mensagem} other {as # mensagens}} do spam? Não é possível desfazer." + "empty_spam_confirm": "Excluir para sempre {count, plural, one {a mensagem} other {as # mensagens}} do spam? Não é possível desfazer.", + "title_all_accounts": "{name} ({count}, {total} em todas as contas)", + "title_all_accounts_only": "{name} ({total} em todas as contas)" }, "uxs_folders": { "manage_accounts": "Gerir contas…", diff --git a/locales/ro/common.json b/locales/ro/common.json index c7cd5c559..91929ed8c 100644 --- a/locales/ro/common.json +++ b/locales/ro/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Golește coșul de gunoi", "empty_spam": "Golește spamul", "empty_trash_confirm": "Ștergeți definitiv {count, plural, one {mesajul} few {toate cele # mesaje} other {toate cele # de mesaje}} din coșul de gunoi? Nu se poate anula.", - "empty_spam_confirm": "Ștergeți definitiv {count, plural, one {mesajul} few {toate cele # mesaje} other {toate cele # de mesaje}} din spam? Nu se poate anula." + "empty_spam_confirm": "Ștergeți definitiv {count, plural, one {mesajul} few {toate cele # mesaje} other {toate cele # de mesaje}} din spam? Nu se poate anula.", + "title_all_accounts": "{name} ({count}, {total} în toate conturile)", + "title_all_accounts_only": "{name} ({total} în toate conturile)" }, "uxs_folders": { "manage_accounts": "Gestionați conturile…", diff --git a/locales/ru/common.json b/locales/ru/common.json index bf469044a..0abdc507b 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Очистить корзину", "empty_spam": "Очистить спам", "empty_trash_confirm": "Удалить {count, plural, =1 {письмо} one {все # письмо} few {все # письма} many {все # писем} other {все # письма}} в корзине навсегда? Это нельзя отменить.", - "empty_spam_confirm": "Удалить {count, plural, =1 {письмо} one {все # письмо} few {все # письма} many {все # писем} other {все # письма}} в спаме навсегда? Это нельзя отменить." + "empty_spam_confirm": "Удалить {count, plural, =1 {письмо} one {все # письмо} few {все # письма} many {все # писем} other {все # письма}} в спаме навсегда? Это нельзя отменить.", + "title_all_accounts": "{name} ({count}, {total} во всех аккаунтах)", + "title_all_accounts_only": "{name} ({total} во всех аккаунтах)" }, "uxs_folders": { "manage_accounts": "Управление аккаунтами…", diff --git a/locales/sk/common.json b/locales/sk/common.json index 006f1981f..4e2e1885f 100644 --- a/locales/sk/common.json +++ b/locales/sk/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Vyprázdniť kôš", "empty_spam": "Vyprázdniť spam", "empty_trash_confirm": "Natrvalo odstrániť {count, plural, one {správu} few {všetky # správy} many {všetkých # správy} other {všetkých # správ}} v koši? Nedá sa to vrátiť späť.", - "empty_spam_confirm": "Natrvalo odstrániť {count, plural, one {správu} few {všetky # správy} many {všetkých # správy} other {všetkých # správ}} v spame? Nedá sa to vrátiť späť." + "empty_spam_confirm": "Natrvalo odstrániť {count, plural, one {správu} few {všetky # správy} many {všetkých # správy} other {všetkých # správ}} v spame? Nedá sa to vrátiť späť.", + "title_all_accounts": "{name} ({count}, {total} vo všetkých účtoch)", + "title_all_accounts_only": "{name} ({total} vo všetkých účtoch)" }, "uxs_folders": { "manage_accounts": "Spravovať účty…", diff --git a/locales/th/common.json b/locales/th/common.json index ff2221ad5..ddfcaab12 100644 --- a/locales/th/common.json +++ b/locales/th/common.json @@ -1034,7 +1034,9 @@ "empty_trash": "ล้างถังขยะ", "empty_spam": "ล้างสแปม", "empty_trash_confirm": "ลบข้อความ {count, plural, other {ทั้ง # ฉบับ}} ในถังขยะอย่างถาวรไหม ย้อนกลับไม่ได้", - "empty_spam_confirm": "ลบข้อความ {count, plural, other {ทั้ง # ฉบับ}} ในสแปมอย่างถาวรไหม ย้อนกลับไม่ได้" + "empty_spam_confirm": "ลบข้อความ {count, plural, other {ทั้ง # ฉบับ}} ในสแปมอย่างถาวรไหม ย้อนกลับไม่ได้", + "title_all_accounts": "{name} ({count}, {total} ในทุกบัญชี)", + "title_all_accounts_only": "{name} ({total} ในทุกบัญชี)" }, "uxs_folders": { "manage_accounts": "จัดการบัญชี…", diff --git a/locales/tr/common.json b/locales/tr/common.json index 473a1eb40..6e66085fc 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Çöp Kutusunu boşalt", "empty_spam": "Spam klasörünü boşalt", "empty_trash_confirm": "Çöp Kutusundaki {count, plural, one {ileti} other {# iletinin tümü}} kalıcı olarak silinsin mi? Bu geri alınamaz.", - "empty_spam_confirm": "Spam klasöründeki {count, plural, one {ileti} other {# iletinin tümü}} kalıcı olarak silinsin mi? Bu geri alınamaz." + "empty_spam_confirm": "Spam klasöründeki {count, plural, one {ileti} other {# iletinin tümü}} kalıcı olarak silinsin mi? Bu geri alınamaz.", + "title_all_accounts": "{name} ({count}, tüm hesaplarda {total})", + "title_all_accounts_only": "{name} (tüm hesaplarda {total})" }, "uxs_folders": { "manage_accounts": "Hesapları yönet…", diff --git a/locales/uk/common.json b/locales/uk/common.json index 3922dca1a..f50025150 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "Очистити кошик", "empty_spam": "Очистити спам", "empty_trash_confirm": "Видалити {count, plural, =1 {лист} one {усі # лист} few {усі # листи} many {усі # листів} other {усі # листа}} в кошику назавжди? Це не можна скасувати.", - "empty_spam_confirm": "Видалити {count, plural, =1 {лист} one {усі # лист} few {усі # листи} many {усі # листів} other {усі # листа}} у спамі назавжди? Це не можна скасувати." + "empty_spam_confirm": "Видалити {count, plural, =1 {лист} one {усі # лист} few {усі # листи} many {усі # листів} other {усі # листа}} у спамі назавжди? Це не можна скасувати.", + "title_all_accounts": "{name} ({count}, {total} в усіх акаунтах)", + "title_all_accounts_only": "{name} ({total} в усіх акаунтах)" }, "uxs_folders": { "manage_accounts": "Керувати обліковими записами…", diff --git a/locales/vi/common.json b/locales/vi/common.json index 26e76d071..9445268ed 100644 --- a/locales/vi/common.json +++ b/locales/vi/common.json @@ -3850,7 +3850,9 @@ "empty_trash": "Dọn sạch Thùng rác", "empty_spam": "Dọn sạch Thư rác", "empty_trash_confirm": "Xóa vĩnh viễn {count, plural, other {tất cả # thư}} trong Thùng rác? Không thể hoàn tác.", - "empty_spam_confirm": "Xóa vĩnh viễn {count, plural, other {tất cả # thư}} trong Thư rác? Không thể hoàn tác." + "empty_spam_confirm": "Xóa vĩnh viễn {count, plural, other {tất cả # thư}} trong Thư rác? Không thể hoàn tác.", + "title_all_accounts": "{name} ({count}, {total} trong mọi tài khoản)", + "title_all_accounts_only": "{name} ({total} trong mọi tài khoản)" }, "uxs_folders": { "manage_accounts": "Quản lý tài khoản…", diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index 5dcbc14dc..a1ed7f1b2 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "清空垃圾桶", "empty_spam": "清空垃圾郵件", "empty_trash_confirm": "要永久刪除垃圾桶中的{count, plural, other {全部 # 封郵件}}嗎?此動作無法復原。", - "empty_spam_confirm": "要永久刪除垃圾郵件中的{count, plural, other {全部 # 封郵件}}嗎?此動作無法復原。" + "empty_spam_confirm": "要永久刪除垃圾郵件中的{count, plural, other {全部 # 封郵件}}嗎?此動作無法復原。", + "title_all_accounts": "{name} ({count}、所有帳號共 {total})", + "title_all_accounts_only": "{name} (所有帳號共 {total})" }, "uxs_folders": { "manage_accounts": "管理帳號…", diff --git a/locales/zh/common.json b/locales/zh/common.json index 0354ee479..8320f27ab 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -3838,7 +3838,9 @@ "empty_trash": "清空已删除", "empty_spam": "清空垃圾邮件", "empty_trash_confirm": "要永久删除已删除中的{count, plural, other {全部 # 封邮件}}吗?此操作无法撤销。", - "empty_spam_confirm": "要永久删除垃圾邮件中的{count, plural, other {全部 # 封邮件}}吗?此操作无法撤销。" + "empty_spam_confirm": "要永久删除垃圾邮件中的{count, plural, other {全部 # 封邮件}}吗?此操作无法撤销。", + "title_all_accounts": "{name} ({count}、所有账户共 {total})", + "title_all_accounts_only": "{name} (所有账户共 {total})" }, "uxs_folders": { "manage_accounts": "管理账户…",