From be5e2dbc5320156944060ae55caf04b83ffdae5e Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 29 Oct 2026 15:37:37 +0100 Subject: [PATCH] feat: hold toasts while focused and jump to the newest with Alt+Shift+N --- .../ui/__tests__/toast-keyboard.test.tsx | 102 ++++++++++++++++++ components/ui/toast.tsx | 92 ++++++++++++++-- lib/keyboard-shortcuts/index.ts | 2 + locales/ar/common.json | 15 +-- locales/ca/common.json | 15 +-- locales/cs/common.json | 15 +-- locales/da/common.json | 15 +-- locales/de/common.json | 3 +- locales/en/common.json | 3 +- locales/es/common.json | 15 +-- locales/et/common.json | 3 +- locales/fa/common.json | 15 +-- locales/fr/common.json | 15 +-- locales/he/common.json | 15 +-- locales/hu/common.json | 15 +-- locales/it/common.json | 15 +-- locales/ja/common.json | 3 +- locales/ko/common.json | 15 +-- locales/lv/common.json | 15 +-- locales/mn/common.json | 15 +-- locales/nb/common.json | 15 +-- locales/nl/common.json | 15 +-- locales/pl/common.json | 15 +-- locales/pt/common.json | 15 +-- locales/ro/common.json | 15 +-- locales/ru/common.json | 15 +-- locales/sk/common.json | 15 +-- locales/th/common.json | 3 +- locales/tr/common.json | 15 +-- locales/uk/common.json | 15 +-- locales/zh-TW/common.json | 15 +-- locales/zh/common.json | 15 +-- 32 files changed, 389 insertions(+), 182 deletions(-) create mode 100644 components/ui/__tests__/toast-keyboard.test.tsx diff --git a/components/ui/__tests__/toast-keyboard.test.tsx b/components/ui/__tests__/toast-keyboard.test.tsx new file mode 100644 index 000000000..f723cb3fd --- /dev/null +++ b/components/ui/__tests__/toast-keyboard.test.tsx @@ -0,0 +1,102 @@ +import { act, fireEvent, render, screen, within } from '@testing-library/react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { ToastContainer } from '../toast'; +import { toast, useToastStore } from '@/stores/toast-store'; + +function Toasts() { + const toasts = useToastStore((s) => s.toasts); + const removeToast = useToastStore((s) => s.removeToast); + return ; +} + +// The toasts themselves, not the live region that reads them out. +const toasts = () => within(document.querySelector('[data-toast-container]') as HTMLElement); +const pressFocusShortcut = () => + fireEvent.keyDown(window, { key: 'N', code: 'KeyN', altKey: true, shiftKey: true }); + +describe('toasts and the keyboard', () => { + beforeEach(() => { + vi.useFakeTimers(); + useToastStore.setState({ toasts: [] }); + }); + const extra: HTMLElement[] = []; + const add = (el: T): T => { + document.body.appendChild(el); + extra.push(el); + return el; + }; + afterEach(() => { + vi.useRealTimers(); + for (const el of extra.splice(0)) el.remove(); + }); + + it('holds the countdown while the focus is inside the toast', () => { + render(); + const onClick = vi.fn(); + act(() => { toast.success('Email archived', { action: { label: 'Undo', onClick }, duration: 5000 }); }); + const undo = screen.getByRole('button', { name: 'Undo' }); + act(() => { undo.focus(); }); + act(() => { vi.advanceTimersByTime(20000); }); + expect(toasts().getByText('Email archived')).toBeInTheDocument(); + + // Focus leaves: the rest of the time runs out. + act(() => { undo.blur(); }); + act(() => { vi.advanceTimersByTime(6000); }); + expect(toasts().queryByText('Email archived')).not.toBeInTheDocument(); + }); + + it('does not hold a toast whose action ends at a fixed time', () => { + render(); + act(() => { + toast.info('Sending', { action: { label: 'Undo', onClick: vi.fn() }, duration: 5000, pauseOnHover: false }); + }); + act(() => { screen.getByRole('button', { name: 'Undo' }).focus(); }); + act(() => { vi.advanceTimersByTime(6000); }); + expect(toasts().queryByText('Sending')).not.toBeInTheDocument(); + }); + + it('moves the focus to the newest toast\'s action with Alt+Shift+N, and back when it closes', () => { + const before = document.createElement('button'); + before.textContent = 'Message'; + add(before); + before.focus(); + render(); + const onClick = vi.fn(); + act(() => { toast.info('Older'); }); + act(() => { toast.success('Email deleted', { action: { label: 'Undo', onClick } }); }); + + pressFocusShortcut(); + expect(document.activeElement).toBe(screen.getByRole('button', { name: 'Undo' })); + + fireEvent.click(screen.getByRole('button', { name: 'Undo' })); + expect(onClick).toHaveBeenCalled(); + expect(document.activeElement).toBe(before); + }); + + it('closes the focused toast with Escape without the page hearing it', () => { + const pageEscape = vi.fn(); + window.addEventListener('keydown', pageEscape); + render(); + act(() => { toast.success('Saved'); }); + pressFocusShortcut(); + pageEscape.mockClear(); + fireEvent.keyDown(document.activeElement!, { key: 'Escape' }); + act(() => { vi.advanceTimersByTime(400); }); + expect(toasts().queryByText('Saved')).not.toBeInTheDocument(); + expect(pageEscape).not.toHaveBeenCalled(); + window.removeEventListener('keydown', pageEscape); + }); + + it('leaves the focus alone while a modal dialog is open', () => { + const dialog = document.createElement('div'); + dialog.setAttribute('aria-modal', 'true'); + const inside = document.createElement('button'); + dialog.appendChild(inside); + add(dialog); + inside.focus(); + render(); + act(() => { toast.success('Saved', { action: { label: 'Undo', onClick: vi.fn() } }); }); + pressFocusShortcut(); + expect(document.activeElement).toBe(inside); + }); +}); diff --git a/components/ui/toast.tsx b/components/ui/toast.tsx index e169b86eb..aa1fcd340 100644 --- a/components/ui/toast.tsx +++ b/components/ui/toast.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useState, useCallback, useRef } from "react"; +import { useEffect, useState, useCallback, useRef, useId } from "react"; import { createPortal } from "react-dom"; import { useTranslations } from "next-intl"; import { X, CheckCircle, AlertCircle, Info, AlertTriangle, Loader2 } from "@/components/icons"; @@ -55,6 +55,21 @@ interface ToastProps { onClose: (id: string) => void; /** Kept out of sight for now (a menu is open over it): the countdown waits. */ held?: boolean; + /** The toast goes away with the keyboard focus in it: put the focus back. */ + onFocusLost?: () => void; +} + +/** + * Jumps to the newest toast from anywhere (Alt+Shift+N), so its Undo can be + * reached without tabbing through the page. Listed in lib/keyboard-shortcuts. + */ +export function isFocusToastShortcut(e: KeyboardEvent): boolean { + return e.altKey && e.shiftKey && !e.ctrlKey && !e.metaKey && e.code === "KeyN"; +} + +/** A modal dialog is open: keys and focus belong to it. */ +function modalOpen(): boolean { + return document.querySelector('[aria-modal="true"]') !== null; } const icons = { @@ -83,7 +98,7 @@ const progressBarStyles = { // A sideways swipe this far (px) on a touch screen dismisses the toast. const SWIPE_DISMISS_DISTANCE = 72; -export function ToastItem({ toast, onClose, held = false }: ToastProps) { +export function ToastItem({ toast, onClose, held = false, onFocusLost }: ToastProps) { const tCommon = useTranslations("common"); const Icon = icons[toast.type]; const [exiting, setExiting] = useState(false); @@ -91,7 +106,14 @@ export function ToastItem({ toast, onClose, held = false }: ToastProps) { const [dragX, setDragX] = useState(0); const [flungTo, setFlungTo] = useState(null); const swipeRef = useRef<{ id: number; x: number; y: number; horizontal: boolean | null } | null>(null); + // Swiping holds the countdown; so do the pointer over the toast and the + // keyboard focus in it (2.2.1), unless the action stops working at a fixed + // time (pauseOnHover: false). const [paused, setPaused] = useState(false); + const [hovered, setHovered] = useState(false); + const [focused, setFocused] = useState(false); + const rootRef = useRef(null); + const titleId = useId(); const remainingRef = useRef(toast.duration ?? 5000); const startRef = useRef(Date.now()); const timerRef = useRef | null>(null); @@ -106,8 +128,10 @@ export function ToastItem({ toast, onClose, held = false }: ToastProps) { const dismiss = useCallback(() => { setExiting(true); + // Removing the toast would drop the focus onto . + if (rootRef.current?.contains(document.activeElement)) onFocusLost?.(); setTimeout(() => onClose(toast.id), 280); - }, [onClose, toast.id]); + }, [onClose, toast.id, onFocusLost]); const dismissByUser = useCallback(() => { try { @@ -118,7 +142,8 @@ export function ToastItem({ toast, onClose, held = false }: ToastProps) { dismiss(); }, [toast, dismiss]); - const waiting = paused || held; + const canPause = toast.pauseOnHover !== false; + const waiting = paused || held || (canPause && (hovered || focused)); useEffect(() => { if (!toast.duration || toast.duration <= 0 || isProgress) return; @@ -217,8 +242,22 @@ export function ToastItem({ toast, onClose, held = false }: ToastProps) { onPointerMove={onPointerMove} onPointerUp={endSwipe} onPointerCancel={endSwipe} - onMouseEnter={() => { if (toast.pauseOnHover !== false) setPaused(true); }} - onMouseLeave={() => setPaused(false)} + ref={rootRef} + role="group" + aria-labelledby={titleId} + tabIndex={-1} + onMouseEnter={() => setHovered(true)} + onMouseLeave={() => setHovered(false)} + onFocus={() => setFocused(true)} + onBlur={(e) => { + if (!e.currentTarget.contains(e.relatedTarget as Node | null)) setFocused(false); + }} + onKeyDown={(e) => { + if (e.key !== "Escape") return; + // The toast's, not the page's Escape (closing the open message). + e.stopPropagation(); + dismissByUser(); + }} onClick={() => { if (swiped) return; if (toast.onClick && !toast.action) { @@ -238,7 +277,7 @@ export function ToastItem({ toast, onClose, held = false }: ToastProps) { {/* Content */}
-

{toast.title}

+

{toast.title}

{toast.message && (

{toast.message}

)} @@ -261,6 +300,7 @@ export function ToastItem({ toast, onClose, held = false }: ToastProps) { {/* Close button. Phones swipe the toast away instead. */}
@@ -318,11 +358,44 @@ export function ToastContainer({ toasts, onClose }: { toasts: Toast[]; onClose: const menuOpen = useContextMenuOpen(); const phone = useMediaQuery("(max-width: 639px)"); const held = menuOpen && phone; + const containerRef = useRef(null); + // Where the focus was before the shortcut moved it into a toast. + const returnFocusRef = useRef(null); useEffect(() => { setMounted(true); }, []); + const hasToasts = toasts.length > 0; + useEffect(() => { + if (!hasToasts) return; + const onKeyDown = (e: KeyboardEvent) => { + if (!isFocusToastShortcut(e) || modalOpen()) return; + const items = containerRef.current?.querySelectorAll(".toast-item"); + const newest = items?.[items.length - 1]; + if (!newest) return; + e.preventDefault(); + const active = document.activeElement as HTMLElement | null; + if (active && active !== document.body && !containerRef.current?.contains(active)) { + returnFocusRef.current = active; + } + // Its first action (Undo), else the close button, else the toast. + const target = + newest.querySelector("button:not([data-toast-close])") ?? + Array.from(newest.querySelectorAll("button[data-toast-close]")).find((b) => b.offsetParent !== null || b.getClientRects().length > 0) ?? + newest; + target.focus(); + }; + window.addEventListener("keydown", onKeyDown); + return () => window.removeEventListener("keydown", onKeyDown); + }, [hasToasts]); + + const returnFocus = useCallback(() => { + const back = returnFocusRef.current; + returnFocusRef.current = null; + if (back && back.isConnected) back.focus(); + }, []); + if (!mounted) return null; return createPortal( @@ -341,6 +414,7 @@ export function ToastContainer({ toasts, onClose }: { toasts: Toast[]; onClose: // Not a live region: the toast store reads each toast out through // lib/announce (errors assertively). A labelled region while toasts // are up, so screen reader users can find them. + ref={containerRef} data-toast-container="" role={toasts.length > 0 ? "region" : undefined} aria-label={toasts.length > 0 ? tCenter("title") : undefined} @@ -348,7 +422,7 @@ export function ToastContainer({ toasts, onClose }: { toasts: Toast[]; onClose: > {toasts.map((toast) => (
- +
))} , diff --git a/lib/keyboard-shortcuts/index.ts b/lib/keyboard-shortcuts/index.ts index 52b36770b..b180d959c 100644 --- a/lib/keyboard-shortcuts/index.ts +++ b/lib/keyboard-shortcuts/index.ts @@ -34,6 +34,8 @@ export const GENERAL_SHORTCUTS: readonly ShortcutEntry[] = [ { key: "Mod + K", description: "command_palette.open", shell: "standard" }, { key: "?", description: "shortcuts.global.help" }, { key: "Alt + Shift + A", description: "shortcuts.global.accessibility_panel" }, + // components/ui/toast.tsx (isFocusToastShortcut) + { key: "Alt + Shift + N", description: "shortcuts.global.focus_notification" }, ]; /** An app's sections with the general keys added (see GENERAL_SHORTCUTS). */ diff --git a/locales/ar/common.json b/locales/ar/common.json index 2fad1f36f..f33ed7f44 100644 --- a/locales/ar/common.json +++ b/locales/ar/common.json @@ -3400,12 +3400,12 @@ "manage": "إدارة القواعد", "senders_count": "{count, plural, one {مرسل واحد} two {مرسلَين} few {# مرسلين} many {# مرسلًا} other {# مرسل}}", "cross_account": "حدد رسائل من حساب واحد", - "opaque_hint": "تم تعديل مرشحاتك يدويًا. افتح إعدادات المرشحات" , - "loading": "جارٍ تحميل القواعد…", - "load_failed": "تعذّر تحميل القواعد", - "none": "لا توجد قواعد بعد", - "new_mail_only": "للرسائل الجديدة فقط", - "running": "جارٍ تشغيل «{name}» على {folder}…" + "opaque_hint": "تم تعديل مرشحاتك يدويًا. افتح إعدادات المرشحات" , + "loading": "جارٍ تحميل القواعد…", + "load_failed": "تعذّر تحميل القواعد", + "none": "لا توجد قواعد بعد", + "new_mail_only": "للرسائل الجديدة فقط", + "running": "جارٍ تشغيل «{name}» على {folder}…" }, "find_emails_from": "البحث عن رسائل من {name}" }, @@ -3496,7 +3496,8 @@ "select_all": "تحديد الكل", "global_search": "البحث في كل شيء", "extend_selection": "توسيع التحديد لأسفل / لأعلى", - "accessibility_panel": "فتح خيارات إمكانية الوصول" + "accessibility_panel": "فتح خيارات إمكانية الوصول", + "focus_notification": "الانتقال إلى أحدث إشعار" }, "threads": { "expand_collapse": "توسيع/طي المحادثة", diff --git a/locales/ca/common.json b/locales/ca/common.json index 013a424cc..c7240ff2a 100644 --- a/locales/ca/common.json +++ b/locales/ca/common.json @@ -3400,12 +3400,12 @@ "manage": "Gestiona les regles", "senders_count": "{count, plural, one {# remitent} other {# remitents}}", "cross_account": "Selecciona missatges d'un sol compte", - "opaque_hint": "Els teus filtres s'han editat a mà. Obre la configuració dels filtres" , - "loading": "S'estan carregant les regles…", - "load_failed": "No s'han pogut carregar les regles", - "none": "Encara no hi ha regles", - "new_mail_only": "Només per a missatges nous", - "running": "S'està executant «{name}» a {folder}…" + "opaque_hint": "Els teus filtres s'han editat a mà. Obre la configuració dels filtres" , + "loading": "S'estan carregant les regles…", + "load_failed": "No s'han pogut carregar les regles", + "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}" }, @@ -3496,7 +3496,8 @@ "select_all": "Selecciona-ho tot", "global_search": "Cerca-ho tot", "extend_selection": "Amplia la selecció cap avall / amunt", - "accessibility_panel": "Obre les opcions d'accessibilitat" + "accessibility_panel": "Obre les opcions d'accessibilitat", + "focus_notification": "Ves a la notificació més recent" }, "threads": { "expand_collapse": "Desplega/replega el fil", diff --git a/locales/cs/common.json b/locales/cs/common.json index b7f885500..4225ca9ff 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -3400,12 +3400,12 @@ "manage": "Spravovat pravidla", "senders_count": "{count, plural, one {# odesílatele} few {# odesílatelů} many {# odesílatele} other {# odesílatelů}}", "cross_account": "Vyberte zprávy z jednoho účtu", - "opaque_hint": "Vaše filtry byly upraveny ručně. Otevřete nastavení filtrů" , - "loading": "Načítání pravidel…", - "load_failed": "Pravidla se nepodařilo načíst", - "none": "Zatím žádná pravidla", - "new_mail_only": "Pouze pro nové zprávy", - "running": "Spouštění „{name}“ ve složce {folder}…" + "opaque_hint": "Vaše filtry byly upraveny ručně. Otevřete nastavení filtrů" , + "loading": "Načítání pravidel…", + "load_failed": "Pravidla se nepodařilo načíst", + "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}" }, @@ -3496,7 +3496,8 @@ "select_all": "Vybrat vše", "global_search": "Hledat ve všem", "extend_selection": "Rozšířit výběr dolů / nahoru", - "accessibility_panel": "Otevřít možnosti přístupnosti" + "accessibility_panel": "Otevřít možnosti přístupnosti", + "focus_notification": "Přejít na nejnovější oznámení" }, "threads": { "expand_collapse": "Rozbalit/sbalit vlákno", diff --git a/locales/da/common.json b/locales/da/common.json index aeef641ae..c60465aa5 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -3400,12 +3400,12 @@ "manage": "Administrer regler", "senders_count": "{count, plural, one {# afsender} other {# afsendere}}", "cross_account": "Vælg beskeder fra én konto", - "opaque_hint": "Dine filtre er redigeret manuelt. Åbn filterindstillingerne" , - "loading": "Indlæser regler…", - "load_failed": "Reglerne kunne ikke indlæses", - "none": "Ingen regler endnu", - "new_mail_only": "Kun for nye beskeder", - "running": "Kører „{name}“ på {folder}…" + "opaque_hint": "Dine filtre er redigeret manuelt. Åbn filterindstillingerne" , + "loading": "Indlæser regler…", + "load_failed": "Reglerne kunne ikke indlæses", + "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}" }, @@ -3496,7 +3496,8 @@ "select_all": "Vælg alle", "global_search": "Søg i alt", "extend_selection": "Udvid markering ned / op", - "accessibility_panel": "Åbn indstillinger for tilgængelighed" + "accessibility_panel": "Åbn indstillinger for tilgængelighed", + "focus_notification": "Gå til den seneste notifikation" }, "threads": { "expand_collapse": "Udvid/skjul tråd", diff --git a/locales/de/common.json b/locales/de/common.json index a86dc77a7..7152e317d 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -3496,7 +3496,8 @@ "select_all": "Alle auswählen", "global_search": "Alles durchsuchen", "extend_selection": "Auswahl nach unten / oben erweitern", - "accessibility_panel": "Optionen zur Barrierefreiheit öffnen" + "accessibility_panel": "Optionen zur Barrierefreiheit öffnen", + "focus_notification": "Zur neuesten Benachrichtigung springen" }, "threads": { "expand_collapse": "Unterhaltung erweitern/einklappen", diff --git a/locales/en/common.json b/locales/en/common.json index eb397ebb4..0802bea66 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -3496,7 +3496,8 @@ "select_all": "Select all", "global_search": "Search everything", "extend_selection": "Extend selection down / up", - "accessibility_panel": "Open the accessibility options" + "accessibility_panel": "Open the accessibility options", + "focus_notification": "Go to the latest notification" }, "threads": { "expand_collapse": "Expand/collapse thread", diff --git a/locales/es/common.json b/locales/es/common.json index b4e3539c9..3bdc412cf 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -3400,12 +3400,12 @@ "manage": "Administrar reglas", "senders_count": "{count, plural, one {# remitente} other {# remitentes}}", "cross_account": "Selecciona mensajes de una sola cuenta", - "opaque_hint": "Tus filtros se editaron a mano. Abre la configuración de filtros" , - "loading": "Cargando reglas…", - "load_failed": "No se pudieron cargar las reglas", - "none": "Aún no hay reglas", - "new_mail_only": "Solo para mensajes nuevos", - "running": "Ejecutando «{name}» en {folder}…" + "opaque_hint": "Tus filtros se editaron a mano. Abre la configuración de filtros" , + "loading": "Cargando reglas…", + "load_failed": "No se pudieron cargar las reglas", + "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}" }, @@ -3496,7 +3496,8 @@ "select_all": "Seleccionar todo", "global_search": "Buscar en todo", "extend_selection": "Ampliar la selección hacia abajo / arriba", - "accessibility_panel": "Abrir las opciones de accesibilidad" + "accessibility_panel": "Abrir las opciones de accesibilidad", + "focus_notification": "Ir a la notificación más reciente" }, "threads": { "expand_collapse": "Expandir/contraer conversación", diff --git a/locales/et/common.json b/locales/et/common.json index 6fd3bd45e..bb3fa3ee6 100644 --- a/locales/et/common.json +++ b/locales/et/common.json @@ -3496,7 +3496,8 @@ "select_all": "Vali kõik", "global_search": "Otsi kõike", "extend_selection": "Laienda valikut alla / üles", - "accessibility_panel": "Ava juurdepääsetavuse valikud" + "accessibility_panel": "Ava juurdepääsetavuse valikud", + "focus_notification": "Mine viimase teavituse juurde" }, "threads": { "expand_collapse": "Ava/sulge vestlus", diff --git a/locales/fa/common.json b/locales/fa/common.json index a36ecdedc..7a0645f19 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -3400,12 +3400,12 @@ "manage": "مدیریت قانون‌ها", "senders_count": "{count, plural, one {# فرستنده} other {# فرستنده}}", "cross_account": "پیام‌هایی از یک حساب انتخاب کنید", - "opaque_hint": "فیلترهای شما دستی ویرایش شده‌اند. تنظیمات فیلترها را باز کنید" , - "loading": "در حال بارگذاری قوانین…", - "load_failed": "بارگذاری قوانین ممکن نشد", - "none": "هنوز قانونی وجود ندارد", - "new_mail_only": "فقط برای پیام‌های جدید", - "running": "در حال اجرای «{name}» روی {folder}…" + "opaque_hint": "فیلترهای شما دستی ویرایش شده‌اند. تنظیمات فیلترها را باز کنید" , + "loading": "در حال بارگذاری قوانین…", + "load_failed": "بارگذاری قوانین ممکن نشد", + "none": "هنوز قانونی وجود ندارد", + "new_mail_only": "فقط برای پیام‌های جدید", + "running": "در حال اجرای «{name}» روی {folder}…" }, "find_emails_from": "یافتن ایمیل‌های {name}" }, @@ -3496,7 +3496,8 @@ "select_all": "انتخاب همه", "global_search": "جستجو در همه جا", "extend_selection": "گسترش انتخاب به پایین / بالا", - "accessibility_panel": "باز کردن گزینه‌های دسترس‌پذیری" + "accessibility_panel": "باز کردن گزینه‌های دسترس‌پذیری", + "focus_notification": "رفتن به آخرین اعلان" }, "threads": { "expand_collapse": "باز/بسته کردن رشته", diff --git a/locales/fr/common.json b/locales/fr/common.json index 0a2a0cb6e..d4931316c 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -3400,12 +3400,12 @@ "manage": "Gérer les règles", "senders_count": "{count, plural, one {# expéditeur} other {# expéditeurs}}", "cross_account": "Sélectionnez des messages d'un seul compte", - "opaque_hint": "Vos filtres ont été modifiés à la main. Ouvrez les réglages des filtres" , - "loading": "Chargement des règles…", - "load_failed": "Impossible de charger les règles", - "none": "Aucune règle pour l'instant", - "new_mail_only": "Uniquement pour les nouveaux messages", - "running": "Exécution de « {name} » sur {folder}…" + "opaque_hint": "Vos filtres ont été modifiés à la main. Ouvrez les réglages des filtres" , + "loading": "Chargement des règles…", + "load_failed": "Impossible de charger les règles", + "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}" }, @@ -3496,7 +3496,8 @@ "select_all": "Tout sélectionner", "global_search": "Tout rechercher", "extend_selection": "Étendre la sélection vers le bas / le haut", - "accessibility_panel": "Ouvrir les options d'accessibilité" + "accessibility_panel": "Ouvrir les options d'accessibilité", + "focus_notification": "Aller à la dernière notification" }, "threads": { "expand_collapse": "Développer/réduire la conversation", diff --git a/locales/he/common.json b/locales/he/common.json index b135a2729..f7756b856 100644 --- a/locales/he/common.json +++ b/locales/he/common.json @@ -3366,12 +3366,12 @@ "manage": "ניהול כללים", "senders_count": "{count, plural, one {שולח אחד} other {# שולחים}}", "cross_account": "בחר הודעות מחשבון אחד", - "opaque_hint": "המסננים שלך נערכו ידנית. פתח את הגדרות המסננים" , - "loading": "טוען כללים…", - "load_failed": "לא ניתן לטעון את הכללים", - "none": "אין עדיין כללים", - "new_mail_only": "רק להודעות חדשות", - "running": "מריץ את „{name}“ על {folder}…" + "opaque_hint": "המסננים שלך נערכו ידנית. פתח את הגדרות המסננים" , + "loading": "טוען כללים…", + "load_failed": "לא ניתן לטעון את הכללים", + "none": "אין עדיין כללים", + "new_mail_only": "רק להודעות חדשות", + "running": "מריץ את „{name}“ על {folder}…" }, "find_emails_from": "חיפוש הודעות מ{name}" }, @@ -3414,7 +3414,8 @@ "select_all": "בחר הכל", "global_search": "חיפוש בכל מקום", "extend_selection": "הרחבת הבחירה למטה / למעלה", - "accessibility_panel": "פתיחת אפשרויות הנגישות" + "accessibility_panel": "פתיחת אפשרויות הנגישות", + "focus_notification": "מעבר להתראה האחרונה" }, "threads": { "expand_collapse": "הרחב/כווץ את השרשור", diff --git a/locales/hu/common.json b/locales/hu/common.json index 038cae002..138b39cdf 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -3400,12 +3400,12 @@ "manage": "Szabályok kezelése", "senders_count": "{count, plural, one {# feladó} other {# feladó}}", "cross_account": "Egyetlen fiók üzeneteit válaszd ki", - "opaque_hint": "A szűrőidet kézzel szerkesztették. Nyisd meg a szűrőbeállításokat" , - "loading": "Szabályok betöltése…", - "load_failed": "Nem sikerült betölteni a szabályokat", - "none": "Még nincsenek szabályok", - "new_mail_only": "Csak új üzenetekre", - "running": "„{name}” futtatása itt: {folder}…" + "opaque_hint": "A szűrőidet kézzel szerkesztették. Nyisd meg a szűrőbeállításokat" , + "loading": "Szabályok betöltése…", + "load_failed": "Nem sikerült betölteni a szabályokat", + "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" }, @@ -3496,7 +3496,8 @@ "select_all": "Összes kijelölése", "global_search": "Keresés mindenben", "extend_selection": "Kijelölés bővítése lefelé / felfelé", - "accessibility_panel": "Kisegítő lehetőségek megnyitása" + "accessibility_panel": "Kisegítő lehetőségek megnyitása", + "focus_notification": "Ugrás a legújabb értesítésre" }, "threads": { "expand_collapse": "Beszélgetés kibontása/összecsukása", diff --git a/locales/it/common.json b/locales/it/common.json index 86fc60844..a8be4eeec 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -3400,12 +3400,12 @@ "manage": "Gestisci regole", "senders_count": "{count, plural, one {# mittente} other {# mittenti}}", "cross_account": "Seleziona messaggi di un solo account", - "opaque_hint": "I tuoi filtri sono stati modificati a mano. Apri le impostazioni dei filtri" , - "loading": "Caricamento delle regole…", - "load_failed": "Impossibile caricare le regole", - "none": "Nessuna regola", - "new_mail_only": "Solo per i nuovi messaggi", - "running": "Esecuzione di «{name}» su {folder}…" + "opaque_hint": "I tuoi filtri sono stati modificati a mano. Apri le impostazioni dei filtri" , + "loading": "Caricamento delle regole…", + "load_failed": "Impossibile caricare le regole", + "none": "Nessuna regola", + "new_mail_only": "Solo per i nuovi messaggi", + "running": "Esecuzione di «{name}» su {folder}…" }, "find_emails_from": "Trova email da {name}" }, @@ -3496,7 +3496,8 @@ "select_all": "Seleziona tutto", "global_search": "Cerca ovunque", "extend_selection": "Estendi la selezione in basso / in alto", - "accessibility_panel": "Apri le opzioni di accessibilità" + "accessibility_panel": "Apri le opzioni di accessibilità", + "focus_notification": "Vai all'ultima notifica" }, "threads": { "expand_collapse": "Espandi/comprimi conversazione", diff --git a/locales/ja/common.json b/locales/ja/common.json index a84c64f46..2a939ca5c 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -3496,7 +3496,8 @@ "select_all": "すべて選択", "global_search": "すべてを検索", "extend_selection": "選択範囲を下 / 上に広げる", - "accessibility_panel": "アクセシビリティのオプションを開く" + "accessibility_panel": "アクセシビリティのオプションを開く", + "focus_notification": "最新の通知に移動" }, "threads": { "expand_collapse": "スレッドの展開/折りたたみ", diff --git a/locales/ko/common.json b/locales/ko/common.json index 38478f6b4..e97fd384c 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -3400,12 +3400,12 @@ "manage": "규칙 관리", "senders_count": "{count, plural, other {보낸 사람 #명}}", "cross_account": "한 계정의 메일만 선택하세요", - "opaque_hint": "필터가 직접 편집되었어요. 필터 설정을 여세요" , - "loading": "규칙을 불러오는 중…", - "load_failed": "규칙을 불러올 수 없습니다", - "none": "아직 규칙이 없습니다", - "new_mail_only": "새 메시지에만 적용", - "running": "{folder}에서 \"{name}\" 실행 중…" + "opaque_hint": "필터가 직접 편집되었어요. 필터 설정을 여세요" , + "loading": "규칙을 불러오는 중…", + "load_failed": "규칙을 불러올 수 없습니다", + "none": "아직 규칙이 없습니다", + "new_mail_only": "새 메시지에만 적용", + "running": "{folder}에서 \"{name}\" 실행 중…" }, "find_emails_from": "{name}님이 보낸 이메일 찾기" }, @@ -3496,7 +3496,8 @@ "select_all": "모두 선택", "global_search": "전체 검색", "extend_selection": "선택 범위를 아래 / 위로 확장", - "accessibility_panel": "접근성 옵션 열기" + "accessibility_panel": "접근성 옵션 열기", + "focus_notification": "최근 알림으로 이동" }, "threads": { "expand_collapse": "대화 펼치기/접기", diff --git a/locales/lv/common.json b/locales/lv/common.json index 56abfc225..dfeca68eb 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -3400,12 +3400,12 @@ "manage": "Pārvaldīt noteikumus", "senders_count": "{count, plural, zero {# sūtītājiem} one {# sūtītāja} other {# sūtītājiem}}", "cross_account": "Izvēlieties vēstules no viena konta", - "opaque_hint": "Jūsu filtri ir rediģēti manuāli. Atveriet filtru iestatījumus" , - "loading": "Ielādē kārtulas…", - "load_failed": "Neizdevās ielādēt kārtulas", - "none": "Vēl nav kārtulu", - "new_mail_only": "Tikai jaunām ziņām", - "running": "Izpilda “{name}” mapē {folder}…" + "opaque_hint": "Jūsu filtri ir rediģēti manuāli. Atveriet filtru iestatījumus" , + "loading": "Ielādē kārtulas…", + "load_failed": "Neizdevās ielādēt kārtulas", + "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}" }, @@ -3496,7 +3496,8 @@ "select_all": "Atlasīt visas", "global_search": "Meklēt visā", "extend_selection": "Paplašināt atlasi uz leju / uz augšu", - "accessibility_panel": "Atvērt pieejamības opcijas" + "accessibility_panel": "Atvērt pieejamības opcijas", + "focus_notification": "Pāriet uz jaunāko paziņojumu" }, "threads": { "expand_collapse": "Izvērst/sairt sarunu", diff --git a/locales/mn/common.json b/locales/mn/common.json index 931f57d05..0cf5c56f2 100644 --- a/locales/mn/common.json +++ b/locales/mn/common.json @@ -3400,12 +3400,12 @@ "manage": "Дүрмүүдийг удирдах", "senders_count": "{count, plural, one {# илгээгч} other {# илгээгч}}", "cross_account": "Нэг бүртгэлийн захиануудыг сонгоно уу", - "opaque_hint": "Таны шүүлтүүрийг гараар засварласан байна. Шүүлтүүрийн тохиргоог нээнэ үү" , - "loading": "Дүрмүүдийг ачаалж байна…", - "load_failed": "Дүрмүүдийг ачаалж чадсангүй", - "none": "Одоогоор дүрэм алга", - "new_mail_only": "Зөвхөн шинэ мессежүүдэд", - "running": "{folder} дээр \"{name}\"-г ажиллуулж байна…" + "opaque_hint": "Таны шүүлтүүрийг гараар засварласан байна. Шүүлтүүрийн тохиргоог нээнэ үү" , + "loading": "Дүрмүүдийг ачаалж байна…", + "load_failed": "Дүрмүүдийг ачаалж чадсангүй", + "none": "Одоогоор дүрэм алга", + "new_mail_only": "Зөвхөн шинэ мессежүүдэд", + "running": "{folder} дээр \"{name}\"-г ажиллуулж байна…" }, "find_emails_from": "{name}-с ирсэн имэйлүүдийг хайх" }, @@ -3496,7 +3496,8 @@ "select_all": "Бүгдийг сонго", "global_search": "Бүгдийг хайх", "extend_selection": "Сонголтыг доош / дээш өргөтгөх", - "accessibility_panel": "Хүртээмжийн сонголтуудыг нээх" + "accessibility_panel": "Хүртээмжийн сонголтуудыг нээх", + "focus_notification": "Хамгийн сүүлийн мэдэгдэл рүү очих" }, "threads": { "expand_collapse": "Хэлхээсийг дэлгэх/хумих", diff --git a/locales/nb/common.json b/locales/nb/common.json index c6f1b5343..7ccd299bc 100644 --- a/locales/nb/common.json +++ b/locales/nb/common.json @@ -3400,12 +3400,12 @@ "manage": "Administrer regler", "senders_count": "{count, plural, one {# avsender} other {# avsendere}}", "cross_account": "Velg meldinger fra én konto", - "opaque_hint": "Filtrene dine er redigert for hånd. Åpne filterinnstillingene" , - "loading": "Laster inn regler…", - "load_failed": "Kunne ikke laste inn reglene", - "none": "Ingen regler ennå", - "new_mail_only": "Bare for nye meldinger", - "running": "Kjører «{name}» på {folder}…" + "opaque_hint": "Filtrene dine er redigert for hånd. Åpne filterinnstillingene" , + "loading": "Laster inn regler…", + "load_failed": "Kunne ikke laste inn reglene", + "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}" }, @@ -3496,7 +3496,8 @@ "select_all": "Velg alle", "global_search": "Søk i alt", "extend_selection": "Utvid markeringen ned / opp", - "accessibility_panel": "Åpne alternativer for tilgjengelighet" + "accessibility_panel": "Åpne alternativer for tilgjengelighet", + "focus_notification": "Gå til det nyeste varselet" }, "threads": { "expand_collapse": "Utvid eller fold sammen tråden", diff --git a/locales/nl/common.json b/locales/nl/common.json index 4d8c6706a..31e921503 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -3400,12 +3400,12 @@ "manage": "Regels beheren", "senders_count": "{count, plural, one {# afzender} other {# afzenders}}", "cross_account": "Selecteer berichten uit één account", - "opaque_hint": "Je filters zijn met de hand bewerkt. Open de filterinstellingen" , - "loading": "Regels laden…", - "load_failed": "De regels konden niet worden geladen", - "none": "Nog geen regels", - "new_mail_only": "Alleen voor nieuwe berichten", - "running": "\"{name}\" wordt uitgevoerd op {folder}…" + "opaque_hint": "Je filters zijn met de hand bewerkt. Open de filterinstellingen" , + "loading": "Regels laden…", + "load_failed": "De regels konden niet worden geladen", + "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" }, @@ -3496,7 +3496,8 @@ "select_all": "Alles selecteren", "global_search": "Alles doorzoeken", "extend_selection": "Selectie omlaag / omhoog uitbreiden", - "accessibility_panel": "Toegankelijkheidsopties openen" + "accessibility_panel": "Toegankelijkheidsopties openen", + "focus_notification": "Naar de nieuwste melding gaan" }, "threads": { "expand_collapse": "Gesprek uitklappen/inklappen", diff --git a/locales/pl/common.json b/locales/pl/common.json index 852631b1e..c325745f5 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -3400,12 +3400,12 @@ "manage": "Zarządzaj regułami", "senders_count": "{count, plural, one {# nadawcy} few {# nadawców} many {# nadawców} other {# nadawcy}}", "cross_account": "Wybierz wiadomości z jednego konta", - "opaque_hint": "Twoje filtry zostały edytowane ręcznie. Otwórz ustawienia filtrów" , - "loading": "Wczytywanie reguł…", - "load_failed": "Nie udało się wczytać reguł", - "none": "Brak reguł", - "new_mail_only": "Tylko dla nowych wiadomości", - "running": "Uruchamianie „{name}” w folderze {folder}…" + "opaque_hint": "Twoje filtry zostały edytowane ręcznie. Otwórz ustawienia filtrów" , + "loading": "Wczytywanie reguł…", + "load_failed": "Nie udało się wczytać reguł", + "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}" }, @@ -3496,7 +3496,8 @@ "select_all": "Zaznacz wszystko", "global_search": "Szukaj wszędzie", "extend_selection": "Rozszerz zaznaczenie w dół / w górę", - "accessibility_panel": "Otwórz opcje dostępności" + "accessibility_panel": "Otwórz opcje dostępności", + "focus_notification": "Przejdź do najnowszego powiadomienia" }, "threads": { "expand_collapse": "Rozwiń/zwiń wątek", diff --git a/locales/pt/common.json b/locales/pt/common.json index 5dbbcca9f..14c8f57c3 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -3400,12 +3400,12 @@ "manage": "Gerenciar regras", "senders_count": "{count, plural, one {# remetente} other {# remetentes}}", "cross_account": "Selecione mensagens de uma única conta", - "opaque_hint": "Seus filtros foram editados manualmente. Abra as configurações de filtros" , - "loading": "A carregar regras…", - "load_failed": "Não foi possível carregar as regras", - "none": "Ainda não há regras", - "new_mail_only": "Apenas para mensagens novas", - "running": "A executar \"{name}\" em {folder}…" + "opaque_hint": "Seus filtros foram editados manualmente. Abra as configurações de filtros" , + "loading": "A carregar regras…", + "load_failed": "Não foi possível carregar as regras", + "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}" }, @@ -3496,7 +3496,8 @@ "select_all": "Selecionar todos", "global_search": "Pesquisar em tudo", "extend_selection": "Expandir a seleção para baixo / cima", - "accessibility_panel": "Abrir as opções de acessibilidade" + "accessibility_panel": "Abrir as opções de acessibilidade", + "focus_notification": "Ir para a notificação mais recente" }, "threads": { "expand_collapse": "Expandir/recolher conversa", diff --git a/locales/ro/common.json b/locales/ro/common.json index 6807bb69d..ca0a17f9d 100644 --- a/locales/ro/common.json +++ b/locales/ro/common.json @@ -3400,12 +3400,12 @@ "manage": "Gestionați regulile", "senders_count": "{count, plural, one {# expeditor} few {# expeditori} other {# de expeditori}}", "cross_account": "Selectați mesaje dintr-un singur cont", - "opaque_hint": "Filtrele dvs. au fost editate manual. Deschideți setările filtrelor" , - "loading": "Se încarcă regulile…", - "load_failed": "Regulile nu au putut fi încărcate", - "none": "Nicio regulă încă", - "new_mail_only": "Doar pentru mesajele noi", - "running": "Se rulează „{name}” în {folder}…" + "opaque_hint": "Filtrele dvs. au fost editate manual. Deschideți setările filtrelor" , + "loading": "Se încarcă regulile…", + "load_failed": "Regulile nu au putut fi încărcate", + "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}" }, @@ -3496,7 +3496,8 @@ "select_all": "Selectează tot", "global_search": "Caută în tot", "extend_selection": "Extinde selecția în jos / în sus", - "accessibility_panel": "Deschide opțiunile de accesibilitate" + "accessibility_panel": "Deschide opțiunile de accesibilitate", + "focus_notification": "Mergi la cea mai recentă notificare" }, "threads": { "expand_collapse": "Extindeți/reduceți firul de discuție", diff --git a/locales/ru/common.json b/locales/ru/common.json index 6c8dbdf80..722d3ae4f 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -3400,12 +3400,12 @@ "manage": "Управление правилами", "senders_count": "{count, plural, one {# отправителя} few {# отправителей} many {# отправителей} other {# отправителя}}", "cross_account": "Выберите письма из одной учётной записи", - "opaque_hint": "Ваши фильтры отредактированы вручную. Откройте настройки фильтров" , - "loading": "Загрузка правил…", - "load_failed": "Не удалось загрузить правила", - "none": "Правил пока нет", - "new_mail_only": "Только для новых писем", - "running": "Выполняется «{name}» в папке {folder}…" + "opaque_hint": "Ваши фильтры отредактированы вручную. Откройте настройки фильтров" , + "loading": "Загрузка правил…", + "load_failed": "Не удалось загрузить правила", + "none": "Правил пока нет", + "new_mail_only": "Только для новых писем", + "running": "Выполняется «{name}» в папке {folder}…" }, "find_emails_from": "Найти письма от {name}" }, @@ -3496,7 +3496,8 @@ "select_all": "Выбрать все", "global_search": "Искать везде", "extend_selection": "Расширить выделение вниз / вверх", - "accessibility_panel": "Открыть параметры специальных возможностей" + "accessibility_panel": "Открыть параметры специальных возможностей", + "focus_notification": "Перейти к последнему уведомлению" }, "threads": { "expand_collapse": "Развернуть/свернуть цепочку", diff --git a/locales/sk/common.json b/locales/sk/common.json index 0fefdddde..89f3e7187 100644 --- a/locales/sk/common.json +++ b/locales/sk/common.json @@ -3400,12 +3400,12 @@ "manage": "Spravovať pravidlá", "senders_count": "{count, plural, one {# odosielateľa} few {# odosielateľov} many {# odosielateľa} other {# odosielateľov}}", "cross_account": "Vyberte správy z jedného účtu", - "opaque_hint": "Vaše filtre boli upravené ručne. Otvorte nastavenia filtrov" , - "loading": "Načítavajú sa pravidlá…", - "load_failed": "Pravidlá sa nepodarilo načítať", - "none": "Zatiaľ žiadne pravidlá", - "new_mail_only": "Len pre nové správy", - "running": "Spúšťa sa „{name}“ v priečinku {folder}…" + "opaque_hint": "Vaše filtre boli upravené ručne. Otvorte nastavenia filtrov" , + "loading": "Načítavajú sa pravidlá…", + "load_failed": "Pravidlá sa nepodarilo načítať", + "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}" }, @@ -3496,7 +3496,8 @@ "select_all": "Vybrať všetko", "global_search": "Hľadať všade", "extend_selection": "Rozšíriť výber nadol / nahor", - "accessibility_panel": "Otvoriť možnosti prístupnosti" + "accessibility_panel": "Otvoriť možnosti prístupnosti", + "focus_notification": "Prejsť na najnovšie upozornenie" }, "threads": { "expand_collapse": "Rozbaliť/zbaliť vlákno", diff --git a/locales/th/common.json b/locales/th/common.json index c4c552dbd..f25eb6147 100644 --- a/locales/th/common.json +++ b/locales/th/common.json @@ -776,7 +776,8 @@ "select_all": "เลือกทั้งหมด", "global_search": "ค้นหาทุกรายการ", "extend_selection": "ขยายการเลือกลง / ขึ้น", - "accessibility_panel": "เปิดตัวเลือกการเข้าถึง" + "accessibility_panel": "เปิดตัวเลือกการเข้าถึง", + "focus_notification": "ไปที่การแจ้งเตือนล่าสุด" }, "threads": { "expand_collapse": "ขยาย/ย่อการสนทนา", diff --git a/locales/tr/common.json b/locales/tr/common.json index 2a2d5c6e9..29c9ebbf0 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -3400,12 +3400,12 @@ "manage": "Kuralları yönet", "senders_count": "{count, plural, one {# gönderen} other {# gönderen}}", "cross_account": "Tek bir hesaptan iletiler seçin", - "opaque_hint": "Filtreleriniz elle düzenlenmiş. Filtre ayarlarını açın" , - "loading": "Kurallar yükleniyor…", - "load_failed": "Kurallar yüklenemedi", - "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…" + "opaque_hint": "Filtreleriniz elle düzenlenmiş. Filtre ayarlarını açın" , + "loading": "Kurallar yükleniyor…", + "load_failed": "Kurallar yüklenemedi", + "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" }, @@ -3496,7 +3496,8 @@ "select_all": "Tümünü seç", "global_search": "Her yerde ara", "extend_selection": "Seçimi aşağı / yukarı genişlet", - "accessibility_panel": "Erişilebilirlik seçeneklerini aç" + "accessibility_panel": "Erişilebilirlik seçeneklerini aç", + "focus_notification": "En son bildirime git" }, "threads": { "expand_collapse": "İleti dizisini genişlet/daralt", diff --git a/locales/uk/common.json b/locales/uk/common.json index 265ad7c59..8a47cdc73 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -3400,12 +3400,12 @@ "manage": "Керувати правилами", "senders_count": "{count, plural, one {# відправника} few {# відправників} many {# відправників} other {# відправника}}", "cross_account": "Виберіть листи з одного облікового запису", - "opaque_hint": "Ваші фільтри відредаговано вручну. Відкрийте налаштування фільтрів" , - "loading": "Завантаження правил…", - "load_failed": "Не вдалося завантажити правила", - "none": "Правил поки немає", - "new_mail_only": "Лише для нових листів", - "running": "Виконується «{name}» у теці {folder}…" + "opaque_hint": "Ваші фільтри відредаговано вручну. Відкрийте налаштування фільтрів" , + "loading": "Завантаження правил…", + "load_failed": "Не вдалося завантажити правила", + "none": "Правил поки немає", + "new_mail_only": "Лише для нових листів", + "running": "Виконується «{name}» у теці {folder}…" }, "find_emails_from": "Знайти листи від {name}" }, @@ -3496,7 +3496,8 @@ "select_all": "Вибрати все", "global_search": "Шукати скрізь", "extend_selection": "Розширити виділення вниз / вгору", - "accessibility_panel": "Відкрити параметри доступності" + "accessibility_panel": "Відкрити параметри доступності", + "focus_notification": "Перейти до останнього сповіщення" }, "threads": { "expand_collapse": "Розгорнути/згорнути ланцюжок", diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index c9b54f025..d02a5e31c 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -3400,12 +3400,12 @@ "manage": "管理規則", "senders_count": "{count, plural, other {# 位寄件者}}", "cross_account": "請選取同一個帳號中的郵件", - "opaque_hint": "你的篩選規則曾被手動編輯。請開啟篩選規則設定" , - "loading": "正在載入規則…", - "load_failed": "無法載入規則", - "none": "尚無規則", - "new_mail_only": "僅限新郵件", - "running": "正在 {folder} 中執行「{name}」…" + "opaque_hint": "你的篩選規則曾被手動編輯。請開啟篩選規則設定" , + "loading": "正在載入規則…", + "load_failed": "無法載入規則", + "none": "尚無規則", + "new_mail_only": "僅限新郵件", + "running": "正在 {folder} 中執行「{name}」…" }, "find_emails_from": "尋找來自 {name} 的郵件" }, @@ -3496,7 +3496,8 @@ "select_all": "全部選取", "global_search": "全域搜尋", "extend_selection": "向下 / 向上擴展選取", - "accessibility_panel": "開啟無障礙選項" + "accessibility_panel": "開啟無障礙選項", + "focus_notification": "前往最新通知" }, "threads": { "expand_collapse": "展開/收合對話串", diff --git a/locales/zh/common.json b/locales/zh/common.json index f5a88e341..70b06591a 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -3400,12 +3400,12 @@ "manage": "管理规则", "senders_count": "{count, plural, other {# 个发件人}}", "cross_account": "请选择同一账户中的邮件", - "opaque_hint": "你的过滤器已被手动编辑。请打开过滤器设置" , - "loading": "正在加载规则…", - "load_failed": "无法加载规则", - "none": "暂无规则", - "new_mail_only": "仅限新邮件", - "running": "正在 {folder} 中运行“{name}”…" + "opaque_hint": "你的过滤器已被手动编辑。请打开过滤器设置" , + "loading": "正在加载规则…", + "load_failed": "无法加载规则", + "none": "暂无规则", + "new_mail_only": "仅限新邮件", + "running": "正在 {folder} 中运行“{name}”…" }, "find_emails_from": "查找来自 {name} 的邮件" }, @@ -3496,7 +3496,8 @@ "select_all": "全选", "global_search": "全局搜索", "extend_selection": "向下 / 向上扩展选择", - "accessibility_panel": "打开无障碍选项" + "accessibility_panel": "打开无障碍选项", + "focus_notification": "转到最新通知" }, "threads": { "expand_collapse": "展开/折叠会话",