feat: add a Sort menu to the mail list header

This commit is contained in:
Linus Rath
2026-10-28 05:16:08 +01:00
parent 91446a807c
commit e3f1ccce75
32 changed files with 510 additions and 0 deletions
@@ -0,0 +1,51 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { useSettingsStore } from '@/stores/settings-store';
import { useAuthStore } from '@/stores/auth-store';
import { useEmailStore } from '@/stores/email-store';
import { MailSortMenu, sortChoiceFor } from '../mail-list-menus';
// The mail list's Sort menu writes the "Message list order" setting, like the
// Files header's sort menu writes its own.
const fetchEmails = vi.fn().mockResolvedValue(undefined);
beforeEach(() => {
fetchEmails.mockClear();
useSettingsStore.setState({ messageListOrder: [], messageListOrderScope: 'inbox' } as never);
useAuthStore.setState({ client: { getEmailQuerySortOptions: () => null } } as never);
useEmailStore.setState({ selectedMailbox: 'sent', fetchEmails } as never);
});
describe('mail list Sort menu', () => {
it('checks the current order', () => {
render(<MailSortMenu folderRole="inbox" />);
fireEvent.click(screen.getByTestId('mail-sort'));
expect(screen.getByTestId('mail-sort-newest').getAttribute('aria-checked')).toBe('true');
expect(screen.getByTestId('mail-sort-oldest').getAttribute('aria-checked')).toBe('false');
});
it('writes the setting, makes it reach this folder, and reloads the list', () => {
render(<MailSortMenu folderRole="sent" />);
fireEvent.click(screen.getByTestId('mail-sort'));
fireEvent.click(screen.getByTestId('mail-sort-oldest'));
const { messageListOrder, messageListOrderScope } = useSettingsStore.getState();
expect(messageListOrder).toEqual([{ criterion: 'receivedAt', direction: 'asc' }]);
// The order applied to the Inbox only, so in Sent the pick did nothing.
expect(messageListOrderScope).toBe('all');
expect(fetchEmails).toHaveBeenCalledWith(expect.anything(), 'sent', { background: true });
});
it('leaves out read state and stars when the server cannot order by them', () => {
useAuthStore.setState({ client: { getEmailQuerySortOptions: () => ['receivedAt', 'from'] } } as never);
render(<MailSortMenu folderRole="inbox" />);
fireEvent.click(screen.getByTestId('mail-sort'));
expect(screen.queryByTestId('mail-sort-unread')).toBeNull();
expect(screen.getByTestId('mail-sort-sender')).toBeTruthy();
});
it('recognises the orders Settings can make', () => {
expect(sortChoiceFor([{ criterion: 'unread', direction: 'desc' }, { criterion: 'receivedAt', direction: 'desc' }])).toBe('unread');
expect(sortChoiceFor([{ criterion: 'unread', direction: 'desc' }, { criterion: 'from', direction: 'asc' }])).toBeNull();
});
});
+138
View File
@@ -0,0 +1,138 @@
"use client";
import { useTranslations } from "next-intl";
import { ArrowUpDown, Check } from "@/components/icons";
import { IconButton } from "@/components/ui/icon-button";
import { ContextMenu, ContextMenuSeparator } from "@/components/ui/context-menu";
import { useContextMenu } from "@/hooks/use-context-menu";
import { useSettingsStore, getMessageListOrderFor } from "@/stores/settings-store";
import { usePolicyStore } from "@/stores/policy-store";
import { useAuthStore } from "@/stores/auth-store";
import { useEmailStore } from "@/stores/email-store";
import { isDefaultOrder, sanitizeSortLevels, type SortLevel } from "@/lib/message-list-order";
import { cn } from "@/lib/utils";
/** The orders the list header offers; Settings has the full editor. */
export const SORT_CHOICES = [
{ key: "newest", levels: [] },
{ key: "oldest", levels: [{ criterion: "receivedAt", direction: "asc" }] },
{ key: "unread", levels: [{ criterion: "unread", direction: "desc" }], keyword: true },
{ key: "starred", levels: [{ criterion: "starred", direction: "desc" }], keyword: true },
{ key: "sender", levels: [{ criterion: "from", direction: "asc" }] },
{ key: "subject", levels: [{ criterion: "subject", direction: "asc" }] },
{ key: "largest", levels: [{ criterion: "size", direction: "desc" }] },
] as const satisfies ReadonlyArray<{ key: string; levels: readonly SortLevel[]; keyword?: boolean }>;
export type SortChoiceKey = (typeof SORT_CHOICES)[number]["key"];
/** The choice `levels` is, or null for an order only Settings can make. */
export function sortChoiceFor(levels: SortLevel[]): SortChoiceKey | null {
// A trailing newest-first level is implicit (see detectPreset).
const last = levels[levels.length - 1];
const core = levels.length > 1 && last.criterion === "receivedAt" && last.direction === "desc"
? levels.slice(0, -1)
: levels;
if (isDefaultOrder(core)) return "newest";
if (core.length !== 1) return null;
const match = SORT_CHOICES.find((choice) =>
choice.levels.length === 1
&& choice.levels[0].criterion === core[0].criterion
&& choice.levels[0].direction === core[0].direction);
return match?.key ?? null;
}
/**
* Picks an order for the list. It writes the "Message list order" setting, so
* Settings and every device agree. That setting applies to the Inbox only
* unless "Apply to all folders" is on; picked in another folder, it turns
* that on, or the pick would do nothing there.
*/
export function applySortChoice(key: SortChoiceKey, folderRole: string | null | undefined): void {
const choice = SORT_CHOICES.find((c) => c.key === key);
if (!choice) return;
const { updateSetting, messageListOrderScope } = useSettingsStore.getState();
updateSetting("messageListOrder", sanitizeSortLevels(choice.levels.map((level) => ({ ...level }))));
if (messageListOrderScope === "inbox" && folderRole !== "inbox" && choice.levels.length > 0) {
updateSetting("messageListOrderScope", "all");
}
const client = useAuthStore.getState().client;
const store = useEmailStore.getState();
if (client && store.selectedMailbox) void store.fetchEmails(client, store.selectedMailbox, { background: true });
}
/** The list header's Sort button (MAIL-30), like the one in Files. */
export function MailSortMenu({ folderRole, disabled, className }: { folderRole: string | null | undefined; disabled?: boolean; className?: string }) {
const t = useTranslations("email_list.sort");
const menu = useContextMenu<null>();
const hidden = usePolicyStore((s) => s.isSettingHidden("messageListOrder"));
const locked = usePolicyStore((s) => s.isSettingLocked("messageListOrder"));
// Re-render when the order changes elsewhere (Settings, another device).
useSettingsStore((s) => s.messageListOrder);
useSettingsStore((s) => s.messageListOrderScope);
const client = useAuthStore((s) => s.client);
if (hidden) return null;
const current = sortChoiceFor(getMessageListOrderFor(folderRole));
// A server without hasKeyword can't order by read state or stars.
const sortOptions = client?.getEmailQuerySortOptions?.() ?? null;
const keywordSort = sortOptions === null || sortOptions.includes("hasKeyword");
return (
<>
<IconButton
label={t("title")}
aria-haspopup="menu"
aria-expanded={menu.contextMenu.isOpen}
disabled={disabled || locked}
className={cn(menu.contextMenu.isOpen && "bg-accent", className)}
data-testid="mail-sort"
onMouseDown={(e) => { if (menu.contextMenu.isOpen) e.stopPropagation(); }}
onClick={(e) => {
if (menu.contextMenu.isOpen) menu.closeContextMenu();
else menu.openContextMenuFrom(e.currentTarget, null, "end");
}}
>
<ArrowUpDown />
</IconButton>
<ContextMenu ref={menu.menuRef} isOpen={menu.contextMenu.isOpen} position={menu.contextMenu.position} onClose={menu.closeContextMenu}>
{SORT_CHOICES.filter((choice) => keywordSort || !("keyword" in choice)).map((choice) => (
<SortItem
key={choice.key}
label={t(choice.key)}
checked={current === choice.key}
testId={`mail-sort-${choice.key}`}
onClick={() => {
menu.closeContextMenu();
applySortChoice(choice.key, folderRole);
}}
/>
))}
{current === null && (
<>
<ContextMenuSeparator />
<SortItem label={t("custom")} checked onClick={menu.closeContextMenu} />
</>
)}
</ContextMenu>
</>
);
}
/** A checkable entry of the Sort menu, as in Files'. */
function SortItem({ label, checked, onClick, testId }: { label: string; checked: boolean; onClick: () => void; testId?: string }) {
return (
<button
type="button"
role="menuitemradio"
aria-checked={checked}
data-testid={testId}
onClick={onClick}
className="w-full px-3 py-1.5 max-md:min-h-11 text-sm text-start flex items-center gap-2 hover:bg-muted focus:outline-none focus-visible:bg-muted"
>
<span className="w-4 h-4 shrink-0 flex items-center justify-center">
{checked && <Check className="w-4 h-4" />}
</span>
<span className="flex-1">{label}</span>
</button>
);
}
+2
View File
@@ -81,6 +81,7 @@ import {
} from "@/components/error";
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
import { ScheduleSendDialog } from "@/components/email/schedule-send-dialog";
import { MailSortMenu } from "@/components/email/mail-list-menus";
import { PromptDialog } from "@/components/ui/prompt-dialog";
import { TotpReauthDialog } from "@/components/totp-reauth-dialog";
import { DragDropProvider } from "@/contexts/drag-drop-context";
@@ -4939,6 +4940,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
</span>
)}
</button>
<MailSortMenu folderRole={conversationRole} disabled={isScheduledView} className="flex-shrink-0 text-muted-foreground" />
<button
type="button"
onClick={handleManualRefresh}
+11
View File
@@ -296,6 +296,17 @@
"more": "مزيد من الإجراءات للمحدد"
},
"skip_to_list": "الانتقال إلى قائمة الرسائل",
"sort": {
"title": "ترتيب",
"newest": "الأحدث أولًا",
"oldest": "الأقدم أولًا",
"unread": "غير المقروءة أولًا",
"starred": "المميّزة بنجمة أولًا",
"sender": "المرسل (أ–ي)",
"subject": "الموضوع (أ–ي)",
"largest": "الأكبر أولًا",
"custom": "ترتيب مخصص (في الإعدادات)"
},
"permanent_delete": "حذف نهائي",
"permanent_delete_confirm_title": "حذف نهائي",
"permanent_delete_confirm_message": "سيتم حذف هذه الرسالة نهائيًا. لا يمكن التراجع عن هذا الإجراء.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Més accions per a la selecció"
},
"skip_to_list": "Salta a la llista de missatges",
"sort": {
"title": "Ordena",
"newest": "Primer els més nous",
"oldest": "Primer els més antics",
"unread": "Primer els no llegits",
"starred": "Primer els destacats",
"sender": "Remitent (A–Z)",
"subject": "Assumpte (A–Z)",
"largest": "Primer els més grans",
"custom": "Ordre personalitzat (a la configuració)"
},
"permanent_delete": "Suprimeix definitivament",
"permanent_delete_confirm_title": "Suprimeix definitivament",
"permanent_delete_confirm_message": "Aquest correu se suprimirà definitivament. Aquesta acció no es pot desfer.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Další akce pro výběr"
},
"skip_to_list": "Přeskočit na seznam zpráv",
"sort": {
"title": "Řazení",
"newest": "Nejnovější nahoře",
"oldest": "Nejstarší nahoře",
"unread": "Nepřečtené nahoře",
"starred": "S hvězdičkou nahoře",
"sender": "Odesílatel (A–Z)",
"subject": "Předmět (A–Z)",
"largest": "Největší nahoře",
"custom": "Vlastní řazení (v nastavení)"
},
"permanent_delete": "Trvale odstranit",
"permanent_delete_confirm_title": "Trvalé odstranění",
"permanent_delete_confirm_message": "Tato zpráva bude trvale odstraněna. Tuto akci nelze vrátit zpět.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Flere handlinger for markeringen"
},
"skip_to_list": "Spring til meddelelseslisten",
"sort": {
"title": "Sortér",
"newest": "Nyeste først",
"oldest": "Ældste først",
"unread": "Ulæste først",
"starred": "Stjernemarkerede først",
"sender": "Afsender (A–Å)",
"subject": "Emne (A–Å)",
"largest": "Største først",
"custom": "Brugerdefineret rækkefølge (i indstillinger)"
},
"permanent_delete": "Slet permanent",
"permanent_delete_confirm_title": "Slet permanent",
"permanent_delete_confirm_message": "Denne e-mail vil blive slettet permanent. Denne handling kan ikke fortrydes.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Weitere Aktionen für die Auswahl"
},
"skip_to_list": "Zur Nachrichtenliste springen",
"sort": {
"title": "Sortieren",
"newest": "Neueste zuerst",
"oldest": "Älteste zuerst",
"unread": "Ungelesene zuerst",
"starred": "Markierte zuerst",
"sender": "Absender (A–Z)",
"subject": "Betreff (A–Z)",
"largest": "Größte zuerst",
"custom": "Eigene Reihenfolge (in den Einstellungen)"
},
"permanent_delete": "Endgültig löschen",
"permanent_delete_confirm_title": "Endgültig löschen",
"permanent_delete_confirm_message": "Diese E-Mail wird endgültig gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.",
+11
View File
@@ -296,6 +296,17 @@
"more": "More actions for the selection"
},
"skip_to_list": "Skip to message list",
"sort": {
"title": "Sort",
"newest": "Newest first",
"oldest": "Oldest first",
"unread": "Unread first",
"starred": "Starred first",
"sender": "Sender (A–Z)",
"subject": "Subject (A–Z)",
"largest": "Largest first",
"custom": "Custom order (in Settings)"
},
"permanent_delete": "Delete permanently",
"permanent_delete_confirm_title": "Permanently delete",
"permanent_delete_confirm_message": "This email will be permanently deleted. This action cannot be undone.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Más acciones para la selección"
},
"skip_to_list": "Saltar a la lista de mensajes",
"sort": {
"title": "Ordenar",
"newest": "Más recientes primero",
"oldest": "Más antiguos primero",
"unread": "No leídos primero",
"starred": "Destacados primero",
"sender": "Remitente (A–Z)",
"subject": "Asunto (A–Z)",
"largest": "Más grandes primero",
"custom": "Orden personalizado (en Ajustes)"
},
"permanent_delete": "Eliminar permanentemente",
"permanent_delete_confirm_title": "Eliminar permanentemente",
"permanent_delete_confirm_message": "Este correo se eliminará permanentemente. Esta acción no se puede deshacer.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Rohkem toiminguid valiku jaoks"
},
"skip_to_list": "Mine kirjade loendisse",
"sort": {
"title": "Sorteeri",
"newest": "Uuemad eespool",
"oldest": "Vanemad eespool",
"unread": "Lugemata eespool",
"starred": "Tärniga eespool",
"sender": "Saatja (A–Z)",
"subject": "Teema (A–Z)",
"largest": "Suuremad eespool",
"custom": "Kohandatud järjestus (seadetes)"
},
"permanent_delete": "Kustuta jäädavalt",
"permanent_delete_confirm_title": "Jäädav kustutamine",
"permanent_delete_confirm_message": "See kiri kustutatakse jäädavalt. Seda toimingut ei saa tagasi võtta.",
+11
View File
@@ -296,6 +296,17 @@
"more": "اقدامات بیشتر برای موارد انتخاب‌شده"
},
"skip_to_list": "رفتن به فهرست پیام‌ها",
"sort": {
"title": "مرتب‌سازی",
"newest": "جدیدترین‌ها اول",
"oldest": "قدیمی‌ترین‌ها اول",
"unread": "خوانده‌نشده‌ها اول",
"starred": "ستاره‌دارها اول",
"sender": "فرستنده (الف–ی)",
"subject": "موضوع (الف–ی)",
"largest": "بزرگ‌ترین‌ها اول",
"custom": "ترتیب سفارشی (در تنظیمات)"
},
"permanent_delete": "حذف دائمی",
"permanent_delete_confirm_title": "حذف دائمی",
"permanent_delete_confirm_message": "این ایمیل برای همیشه حذف خواهد شد. این عملیات قابل بازگشت نیست.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Plus d'actions pour la sélection"
},
"skip_to_list": "Aller à la liste des messages",
"sort": {
"title": "Trier",
"newest": "Plus récents d'abord",
"oldest": "Plus anciens d'abord",
"unread": "Non lus d'abord",
"starred": "Suivis d'abord",
"sender": "Expéditeur (A–Z)",
"subject": "Objet (A–Z)",
"largest": "Plus volumineux d'abord",
"custom": "Ordre personnalisé (dans les paramètres)"
},
"permanent_delete": "Supprimer définitivement",
"permanent_delete_confirm_title": "Supprimer définitivement",
"permanent_delete_confirm_message": "Cet e-mail sera définitivement supprimé. Cette action est irréversible.",
+11
View File
@@ -261,6 +261,17 @@
"selected_messages": "{count, plural, one {דוא״ל 1} other {# דוא״ל}} שנבחרו"
},
"skip_to_list": "דילוג לרשימת ההודעות",
"sort": {
"title": "מיון",
"newest": "החדשות ביותר קודם",
"oldest": "הישנות ביותר קודם",
"unread": "שלא נקראו קודם",
"starred": "מסומנות בכוכב קודם",
"sender": "שולח (א–ת)",
"subject": "נושא (א–ת)",
"largest": "הגדולות ביותר קודם",
"custom": "סדר מותאם (בהגדרות)"
},
"permanent_delete": "מחק לצמיתות",
"permanent_delete_confirm_title": "מחק לצמיתות",
"permanent_delete_confirm_message": "אימייל זה יימחק לצמיתות. לא ניתן לבטל פעולה זו.",
+11
View File
@@ -296,6 +296,17 @@
"more": "További műveletek a kijelöléshez"
},
"skip_to_list": "Ugrás az üzenetlistára",
"sort": {
"title": "Rendezés",
"newest": "Legújabb elöl",
"oldest": "Legrégebbi elöl",
"unread": "Olvasatlanok elöl",
"starred": "Csillagozottak elöl",
"sender": "Feladó (A–Z)",
"subject": "Tárgy (A–Z)",
"largest": "Legnagyobb elöl",
"custom": "Egyéni sorrend (a beállításokban)"
},
"permanent_delete": "Végleges törlés",
"permanent_delete_confirm_title": "Végleges törlés",
"permanent_delete_confirm_message": "Ez az e-mail véglegesen törlődik. Ez a művelet nem vonható vissza.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Altre azioni per la selezione"
},
"skip_to_list": "Vai all'elenco dei messaggi",
"sort": {
"title": "Ordina",
"newest": "Più recenti prima",
"oldest": "Meno recenti prima",
"unread": "Non letti prima",
"starred": "Speciali prima",
"sender": "Mittente (A–Z)",
"subject": "Oggetto (A–Z)",
"largest": "Più grandi prima",
"custom": "Ordine personalizzato (nelle impostazioni)"
},
"permanent_delete": "Elimina definitivamente",
"permanent_delete_confirm_title": "Elimina definitivamente",
"permanent_delete_confirm_message": "Questa e-mail verrà eliminata definitivamente. Questa azione non può essere annullata.",
+11
View File
@@ -296,6 +296,17 @@
"more": "選択項目のその他の操作"
},
"skip_to_list": "メッセージ一覧へ移動",
"sort": {
"title": "並べ替え",
"newest": "新しい順",
"oldest": "古い順",
"unread": "未読を先頭",
"starred": "スター付きを先頭",
"sender": "差出人(A–Z)",
"subject": "件名(A–Z)",
"largest": "サイズの大きい順",
"custom": "カスタムの並び順(設定)"
},
"permanent_delete": "完全に削除",
"permanent_delete_confirm_title": "完全に削除",
"permanent_delete_confirm_message": "このメールは完全に削除されます。この操作は取り消せません。",
+11
View File
@@ -296,6 +296,17 @@
"more": "선택 항목에 대한 추가 작업"
},
"skip_to_list": "메시지 목록으로 건너뛰기",
"sort": {
"title": "정렬",
"newest": "최신순",
"oldest": "오래된순",
"unread": "읽지 않은 메일 먼저",
"starred": "별표 메일 먼저",
"sender": "보낸 사람 (가–하)",
"subject": "제목 (가–하)",
"largest": "큰 메일 먼저",
"custom": "사용자 지정 순서 (설정)"
},
"permanent_delete": "영구 삭제",
"permanent_delete_confirm_title": "영구 삭제",
"permanent_delete_confirm_message": "이 메일은 영구적으로 삭제되며, 다시 복구할 수 없어요.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Vairāk darbību atlasei"
},
"skip_to_list": "Pāriet uz vēstuļu sarakstu",
"sort": {
"title": "Kārtot",
"newest": "Jaunākās vispirms",
"oldest": "Vecākās vispirms",
"unread": "Nelasītās vispirms",
"starred": "Ar zvaigznīti vispirms",
"sender": "Sūtītājs (A–Z)",
"subject": "Temats (A–Z)",
"largest": "Lielākās vispirms",
"custom": "Pielāgota secība (iestatījumos)"
},
"permanent_delete": "Dzēst neatgriezeniski",
"permanent_delete_confirm_title": "Dzēst neatgriezeniski",
"permanent_delete_confirm_message": "Šī vēstule tiks dzēsta neatgriezeniski. Šo darbību nevar atcelt.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Сонгосон зүйлсийн бусад үйлдэл"
},
"skip_to_list": "Захидлын жагсаалт руу шилжих",
"sort": {
"title": "Эрэмбэлэх",
"newest": "Шинэ нь эхэндээ",
"oldest": "Хуучин нь эхэндээ",
"unread": "Уншаагүй нь эхэндээ",
"starred": "Одтой нь эхэндээ",
"sender": "Илгээгч (А–Я)",
"subject": "Гарчиг (А–Я)",
"largest": "Том нь эхэндээ",
"custom": "Тусгай дараалал (тохиргоонд)"
},
"permanent_delete": "Бүрмөсөн устгах",
"permanent_delete_confirm_title": "Бүрмөсөн устгах",
"permanent_delete_confirm_message": "Энэ имэйлийг бүрмөсөн устгах болно. Энэ үйлдлийг буцаах боломжгүй.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Flere handlinger for utvalget"
},
"skip_to_list": "Hopp til meldingslisten",
"sort": {
"title": "Sorter",
"newest": "Nyeste først",
"oldest": "Eldste først",
"unread": "Uleste først",
"starred": "Stjernemerkede først",
"sender": "Avsender (A–Å)",
"subject": "Emne (A–Å)",
"largest": "Største først",
"custom": "Egendefinert rekkefølge (i innstillingene)"
},
"permanent_delete": "Slett permanent",
"permanent_delete_confirm_title": "Slett permanent",
"permanent_delete_confirm_message": "Denne e-posten slettes permanent. Denne handlingen kan ikke angres.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Meer acties voor de selectie"
},
"skip_to_list": "Naar de berichtenlijst",
"sort": {
"title": "Sorteren",
"newest": "Nieuwste eerst",
"oldest": "Oudste eerst",
"unread": "Ongelezen eerst",
"starred": "Met ster eerst",
"sender": "Afzender (A–Z)",
"subject": "Onderwerp (A–Z)",
"largest": "Grootste eerst",
"custom": "Eigen volgorde (in Instellingen)"
},
"permanent_delete": "Definitief verwijderen",
"permanent_delete_confirm_title": "Definitief verwijderen",
"permanent_delete_confirm_message": "Deze e-mail wordt definitief verwijderd. Deze actie kan niet ongedaan worden gemaakt.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Więcej działań dla zaznaczenia"
},
"skip_to_list": "Przejdź do listy wiadomości",
"sort": {
"title": "Sortuj",
"newest": "Od najnowszych",
"oldest": "Od najstarszych",
"unread": "Najpierw nieprzeczytane",
"starred": "Najpierw oznaczone gwiazdką",
"sender": "Nadawca (A–Z)",
"subject": "Temat (A–Z)",
"largest": "Od największych",
"custom": "Własna kolejność (w ustawieniach)"
},
"permanent_delete": "Usuń trwale",
"permanent_delete_confirm_title": "Trwałe usunięcie",
"permanent_delete_confirm_message": "Ta wiadomość zostanie trwale usunięta. Tej operacji nie można cofnąć.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Mais ações para a seleção"
},
"skip_to_list": "Ir para a lista de mensagens",
"sort": {
"title": "Ordenar",
"newest": "Mais recentes primeiro",
"oldest": "Mais antigos primeiro",
"unread": "Não lidos primeiro",
"starred": "Com estrela primeiro",
"sender": "Remetente (A–Z)",
"subject": "Assunto (A–Z)",
"largest": "Maiores primeiro",
"custom": "Ordem personalizada (nas Definições)"
},
"permanent_delete": "Excluir permanentemente",
"permanent_delete_confirm_title": "Excluir permanentemente",
"permanent_delete_confirm_message": "Este e-mail será permanentemente excluído. Esta ação não pode ser desfeita.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Mai multe acțiuni pentru selecție"
},
"skip_to_list": "Salt la lista de mesaje",
"sort": {
"title": "Sortează",
"newest": "Cele mai noi primele",
"oldest": "Cele mai vechi primele",
"unread": "Necitite primele",
"starred": "Cu stea primele",
"sender": "Expeditor (A–Z)",
"subject": "Subiect (A–Z)",
"largest": "Cele mai mari primele",
"custom": "Ordine personalizată (în Setări)"
},
"permanent_delete": "Șterge definitiv",
"permanent_delete_confirm_title": "Șterge definitiv",
"permanent_delete_confirm_message": "Acest e-mail va fi șters definitiv. Această acțiune nu poate fi anulată.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Другие действия с выбранным"
},
"skip_to_list": "Перейти к списку писем",
"sort": {
"title": "Сортировка",
"newest": "Сначала новые",
"oldest": "Сначала старые",
"unread": "Сначала непрочитанные",
"starred": "Сначала помеченные",
"sender": "Отправитель (А–Я)",
"subject": "Тема (А–Я)",
"largest": "Сначала большие",
"custom": "Свой порядок (в настройках)"
},
"permanent_delete": "Удалить навсегда",
"permanent_delete_confirm_title": "Удалить навсегда",
"permanent_delete_confirm_message": "Это письмо будет удалено навсегда. Действие нельзя отменить.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Ďalšie akcie pre výber"
},
"skip_to_list": "Preskočiť na zoznam správ",
"sort": {
"title": "Zoradiť",
"newest": "Najnovšie hore",
"oldest": "Najstaršie hore",
"unread": "Neprečítané hore",
"starred": "S hviezdičkou hore",
"sender": "Odosielateľ (A–Z)",
"subject": "Predmet (A–Z)",
"largest": "Najväčšie hore",
"custom": "Vlastné poradie (v nastaveniach)"
},
"permanent_delete": "Trvalo odstrániť",
"permanent_delete_confirm_title": "Trvalo odstrániť",
"permanent_delete_confirm_message": "Tento e-mail bude trvalo odstránený. Túto akciu nie je možné vrátiť.",
+11
View File
@@ -479,6 +479,17 @@
"more": "การดำเนินการเพิ่มเติมสำหรับรายการที่เลือก"
},
"skip_to_list": "ข้ามไปที่รายการข้อความ",
"sort": {
"title": "จัดเรียง",
"newest": "ใหม่สุดก่อน",
"oldest": "เก่าสุดก่อน",
"unread": "ยังไม่อ่านก่อน",
"starred": "ติดดาวก่อน",
"sender": "ผู้ส่ง (ก–ฮ)",
"subject": "หัวเรื่อง (ก–ฮ)",
"largest": "ใหญ่สุดก่อน",
"custom": "ลำดับที่กำหนดเอง (ในการตั้งค่า)"
},
"permanent_delete": "ลบถาวร",
"permanent_delete_confirm_title": "ยืนยันการลบถาวร",
"permanent_delete_confirm_message": "อีเมลนี้จะถูกลบอย่างถาวรและไม่สามารถกู้คืนได้",
+11
View File
@@ -296,6 +296,17 @@
"more": "Seçim için diğer işlemler"
},
"skip_to_list": "İleti listesine atla",
"sort": {
"title": "Sırala",
"newest": "Önce en yeni",
"oldest": "Önce en eski",
"unread": "Önce okunmamış",
"starred": "Önce yıldızlı",
"sender": "Gönderen (A–Z)",
"subject": "Konu (A–Z)",
"largest": "Önce en büyük",
"custom": "Özel sıralama (Ayarlar'da)"
},
"permanent_delete": "Kalıcı olarak sil",
"permanent_delete_confirm_title": "Kalıcı olarak sil",
"permanent_delete_confirm_message": "Bu e-posta kalıcı olarak silinecek. Bu işlem geri alınamaz.",
+11
View File
@@ -296,6 +296,17 @@
"more": "Інші дії з вибраним"
},
"skip_to_list": "Перейти до списку листів",
"sort": {
"title": "Сортування",
"newest": "Спершу нові",
"oldest": "Спершу старі",
"unread": "Спершу непрочитані",
"starred": "Спершу із зірочкою",
"sender": "Відправник (А–Я)",
"subject": "Тема (А–Я)",
"largest": "Спершу великі",
"custom": "Власний порядок (у налаштуваннях)"
},
"permanent_delete": "Видалити остаточно",
"permanent_delete_confirm_title": "Видалити назавжди",
"permanent_delete_confirm_message": "Цей електронний лист буде видалено назавжди. Цю дію не можна скасувати.",
+11
View File
@@ -296,6 +296,17 @@
"more": "所選項目的更多動作"
},
"skip_to_list": "跳到郵件清單",
"sort": {
"title": "排序",
"newest": "最新優先",
"oldest": "最舊優先",
"unread": "未讀優先",
"starred": "已加星號優先",
"sender": "寄件者(A–Z)",
"subject": "主旨(A–Z)",
"largest": "最大優先",
"custom": "自訂順序(在設定中)"
},
"permanent_delete": "永久刪除",
"permanent_delete_confirm_title": "永久刪除",
"permanent_delete_confirm_message": "這封郵件將永久刪除。此動作無法復原。",
+11
View File
@@ -296,6 +296,17 @@
"more": "所选项目的更多操作"
},
"skip_to_list": "跳到邮件列表",
"sort": {
"title": "排序",
"newest": "最新优先",
"oldest": "最早优先",
"unread": "未读优先",
"starred": "星标优先",
"sender": "发件人(A–Z)",
"subject": "主题(A–Z)",
"largest": "最大优先",
"custom": "自定义顺序(在设置中)"
},
"permanent_delete": "永久删除",
"permanent_delete_confirm_title": "永久删除",
"permanent_delete_confirm_message": "该邮件将被永久删除。此操作无法撤销。",