mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
fix: name every icon button and switch in Settings for what it does
This commit is contained in:
@@ -4,7 +4,8 @@ import { useState, useCallback, useMemo, useRef } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Plus, Trash2 } from "@/components/icons";
|
||||
import { Plus, X } from "@/components/icons";
|
||||
import { IconButton } from "@/components/ui/icon-button";
|
||||
import { Dialog } from "@/components/ui/dialog";
|
||||
import { useDiscardChanges } from "@/components/settings/use-discard-changes";
|
||||
import type {
|
||||
@@ -602,15 +603,14 @@ export function FilterRuleModal({
|
||||
/>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
<IconButton
|
||||
label={t("remove_condition")}
|
||||
onClick={() => removeCondition(index)}
|
||||
disabled={conditions.length <= 1}
|
||||
className="p-1.5 rounded hover:bg-muted text-muted-foreground hover:text-destructive transition-colors disabled:opacity-30 disabled:pointer-events-none"
|
||||
aria-label={t("delete_rule")}
|
||||
className="text-muted-foreground hover:text-destructive disabled:opacity-30"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</button>
|
||||
<X />
|
||||
</IconButton>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -727,15 +727,14 @@ export function FilterRuleModal({
|
||||
</select>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
<IconButton
|
||||
label={t("remove_action")}
|
||||
onClick={() => removeAction(index)}
|
||||
disabled={actions.length <= 1}
|
||||
className="p-1.5 rounded hover:bg-muted text-muted-foreground hover:text-destructive transition-colors disabled:opacity-30 disabled:pointer-events-none"
|
||||
aria-label={t("delete_rule")}
|
||||
className="text-muted-foreground hover:text-destructive disabled:opacity-30"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</button>
|
||||
<X />
|
||||
</IconButton>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { FilterRule } from '@/lib/jmap/sieve-types';
|
||||
import { FilterRuleModal } from '@/components/filters/filter-rule-modal';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useEmailStore } from '@/stores/email-store';
|
||||
import { useFilterStore } from '@/stores/filter-store';
|
||||
import { FilterSettings } from '../filter-settings';
|
||||
|
||||
// SET-11: icon buttons and switches in Settings have a name that says what
|
||||
// they do. The rule dialog's remove buttons were both named "Delete Rule";
|
||||
// the rule switches on the Filters page had none.
|
||||
|
||||
vi.mock('@/stores/toast-store', () => ({ toast: { success: vi.fn(), error: vi.fn(), info: vi.fn() } }));
|
||||
vi.mock('../settings-section', async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import('../settings-section')>()),
|
||||
SettingsSection: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
const rule: FilterRule = {
|
||||
id: 'r1', name: 'Newsletters', enabled: true, matchType: 'all',
|
||||
conditions: [{ field: 'from', comparator: 'contains', value: 'a' }, { field: 'subject', comparator: 'contains', value: 'b' }],
|
||||
actions: [{ type: 'star' }, { type: 'mark_read' }], stopProcessing: false,
|
||||
};
|
||||
|
||||
const initial = { auth: useAuthStore.getState(), email: useEmailStore.getState(), filter: useFilterStore.getState() };
|
||||
afterEach(() => {
|
||||
useAuthStore.setState(initial.auth, true);
|
||||
useEmailStore.setState(initial.email, true);
|
||||
useFilterStore.setState(initial.filter, true);
|
||||
});
|
||||
|
||||
function unnamedButtons(root: HTMLElement) {
|
||||
return Array.from(root.querySelectorAll('button, [role="switch"]')).filter((b) => {
|
||||
const name = b.getAttribute('aria-label') || b.getAttribute('aria-labelledby') || b.textContent?.trim() || b.getAttribute('title');
|
||||
return !name;
|
||||
});
|
||||
}
|
||||
|
||||
describe('names of settings controls', () => {
|
||||
it('names the remove buttons of a rule for what they remove', () => {
|
||||
render(<FilterRuleModal rule={rule} mailboxes={[]} onSave={vi.fn()} onClose={vi.fn()} />);
|
||||
expect(screen.getAllByRole('button', { name: 'remove_condition' })).toHaveLength(2);
|
||||
expect(screen.getAllByRole('button', { name: 'remove_action' })).toHaveLength(2);
|
||||
expect(screen.queryByRole('button', { name: 'delete_rule' })).toBeNull();
|
||||
expect(unnamedButtons(screen.getByRole('dialog'))).toEqual([]);
|
||||
});
|
||||
|
||||
it('names each rule switch on the Filters page after its rule', () => {
|
||||
useAuthStore.setState({ client: { updateSieveScript: vi.fn(), getSieveAccountId: () => 'primary' } as never });
|
||||
useEmailStore.setState({ fetchMailboxes: vi.fn(async () => {}) as never });
|
||||
useFilterStore.setState({
|
||||
rules: [rule], isSupported: true, isLoading: false, isOpaque: false, activeScriptId: 's',
|
||||
selectedAccountId: 'primary', sieveCapabilities: null, selectAccount: vi.fn(async () => {}),
|
||||
});
|
||||
const { container } = render(<FilterSettings />);
|
||||
expect(screen.getByRole('switch', { name: 'Newsletters' })).toBeTruthy();
|
||||
expect(unnamedButtons(container)).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -20,10 +20,12 @@ import { IS_LITE } from '@/lib/lite';
|
||||
import { useConfig } from '@/hooks/use-config';
|
||||
import { totpIssuer } from '@/lib/totp-issuer';
|
||||
import { ConfirmDialog } from '@/components/ui/confirm-dialog';
|
||||
import { IconButton } from '@/components/ui/icon-button';
|
||||
import { useConfirmDialog } from '@/hooks/use-confirm-dialog';
|
||||
|
||||
function PasswordChangeSection() {
|
||||
const t = useTranslations('settings.security');
|
||||
const tLogin = useTranslations('login');
|
||||
const { changePassword, isSaving, otpEnabled } = useAccountSecurityStore();
|
||||
const [currentPassword, setCurrentPassword] = useState('');
|
||||
const [otpCode, setOtpCode] = useState('');
|
||||
@@ -78,13 +80,14 @@ function PasswordChangeSection() {
|
||||
autoComplete="current-password"
|
||||
className="pe-10"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
<IconButton
|
||||
label={showCurrent ? tLogin('hide_password') : tLogin('show_password')}
|
||||
aria-pressed={showCurrent}
|
||||
onClick={() => setShowCurrent(!showCurrent)}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
|
||||
className="absolute end-1 top-1/2 -translate-y-1/2 text-muted-foreground"
|
||||
>
|
||||
{showCurrent ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
||||
</button>
|
||||
{showCurrent ? <EyeOff /> : <Eye />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
{otpEnabled && (
|
||||
@@ -112,13 +115,14 @@ function PasswordChangeSection() {
|
||||
autoComplete="new-password"
|
||||
className="pe-10"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
<IconButton
|
||||
label={showNew ? tLogin('hide_password') : tLogin('show_password')}
|
||||
aria-pressed={showNew}
|
||||
onClick={() => setShowNew(!showNew)}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
|
||||
className="absolute end-1 top-1/2 -translate-y-1/2 text-muted-foreground"
|
||||
>
|
||||
{showNew ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
||||
</button>
|
||||
{showNew ? <EyeOff /> : <Eye />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
@@ -392,7 +396,7 @@ function parseIpList(raw: string): string[] {
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function CredentialRow({ entry, onRemove, isSaving }: { entry: AppPasswordInfo | ApiKeyInfo; onRemove: (id: string) => void; isSaving: boolean }) {
|
||||
function CredentialRow({ entry, onRemove, isSaving, deleteLabel }: { entry: AppPasswordInfo | ApiKeyInfo; onRemove: (id: string) => void; isSaving: boolean; deleteLabel: string }) {
|
||||
return (
|
||||
<div className="flex items-start justify-between py-2 px-3 bg-muted/50 rounded-md gap-2">
|
||||
<div className="flex flex-col min-w-0 flex-1">
|
||||
@@ -416,15 +420,14 @@ function CredentialRow({ entry, onRemove, isSaving }: { entry: AppPasswordInfo |
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
<IconButton
|
||||
label={deleteLabel}
|
||||
onClick={() => onRemove(entry.id)}
|
||||
disabled={isSaving}
|
||||
className="text-destructive hover:text-destructive shrink-0"
|
||||
>
|
||||
<Trash2 className="w-3 h-3" />
|
||||
</Button>
|
||||
<Trash2 />
|
||||
</IconButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -582,7 +585,7 @@ function CredentialSection({ icon: Icon, i18nNamespace, entries, onCreate, onRem
|
||||
{entries.length > 0 ? (
|
||||
<div className="space-y-1">
|
||||
{entries.map((entry) => (
|
||||
<CredentialRow key={entry.id} entry={entry} onRemove={handleRemove} isSaving={isSaving} />
|
||||
<CredentialRow key={entry.id} entry={entry} onRemove={handleRemove} isSaving={isSaving} deleteLabel={tCommon('delete')} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
@@ -869,30 +872,27 @@ function PublicKeysSection() {
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
<IconButton
|
||||
label={isEncryptedWithThisKey ? t('encryption.disable_tooltip') : t('encryption.enable_tooltip')}
|
||||
onClick={() => handleToggleEncryption(key.id)}
|
||||
disabled={isSaving}
|
||||
title={isEncryptedWithThisKey ? t('encryption.disable_tooltip') : t('encryption.enable_tooltip')}
|
||||
className={cn(
|
||||
isEncryptedWithThisKey
|
||||
? 'text-green-600 hover:text-green-700 dark:text-green-400'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
{isEncryptedWithThisKey ? <Lock className="w-4 h-4" /> : <Unlock className="w-4 h-4" />}
|
||||
</Button>
|
||||
{isEncryptedWithThisKey ? <Lock /> : <Unlock />}
|
||||
</IconButton>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
<IconButton
|
||||
label={tCommon('delete')}
|
||||
onClick={() => handleRemove(key.id)}
|
||||
disabled={isSaving || isEncryptedWithThisKey}
|
||||
className="text-destructive hover:text-destructive"
|
||||
>
|
||||
<Trash2 className="w-3 h-3" />
|
||||
</Button>
|
||||
<Trash2 />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -672,6 +672,7 @@ export function FilterSettings() {
|
||||
<ToggleSwitch
|
||||
checked={rule.enabled}
|
||||
onChange={() => handleToggle(rule.id)}
|
||||
ariaLabel={rule.name}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -755,6 +756,7 @@ export function FilterSettings() {
|
||||
<ToggleSwitch
|
||||
checked={expandedFilterView}
|
||||
onChange={(v) => updateSetting("expandedFilterView", v)}
|
||||
ariaLabel={t("expanded_view")}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
type AppIcon,
|
||||
} from '@/components/icons';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { IconButton } from '@/components/ui/icon-button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { AppearanceSettings } from '@/components/settings/appearance-settings';
|
||||
import { AppTopBannerSlot } from '@/components/plugins/app-top-banner-slot';
|
||||
@@ -868,14 +869,9 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
<div className={cn("flex flex-col bg-background pt-[env(safe-area-inset-top)]", isEmbedded ? "h-full" : "h-dvh")}>
|
||||
<AppTopBannerSlot />
|
||||
<div className="flex items-center gap-2 px-4 h-14 border-b border-border bg-background shrink-0">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => window.history.back()}
|
||||
className="h-10 w-10"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</Button>
|
||||
<IconButton label={t('back_to_settings')} size="icon-lg" onClick={() => window.history.back()}>
|
||||
<ArrowLeft />
|
||||
</IconButton>
|
||||
<h1 className="font-semibold text-lg truncate">{activeTabLabel}</h1>
|
||||
</div>
|
||||
|
||||
@@ -901,14 +897,9 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
<div className={cn("flex flex-col bg-background pt-[env(safe-area-inset-top)]", isEmbedded ? "h-full" : "h-dvh")}>
|
||||
<AppTopBannerSlot />
|
||||
<div className="flex items-center gap-2 px-4 h-14 border-b border-border bg-background shrink-0">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => router.push('/')}
|
||||
className="h-10 w-10"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</Button>
|
||||
<IconButton label={t('back_to_mail')} size="icon-lg" onClick={() => router.push('/')}>
|
||||
<ArrowLeft />
|
||||
</IconButton>
|
||||
<div className="flex items-center gap-2">
|
||||
<SettingsIcon className="w-5 h-5 text-muted-foreground" />
|
||||
<h1 className="font-semibold text-lg">{t('title')}</h1>
|
||||
@@ -928,14 +919,13 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
aria-label={t('search_placeholder')}
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button
|
||||
type="button"
|
||||
<IconButton
|
||||
label={t('search_clear')}
|
||||
onClick={() => setSearchQuery('')}
|
||||
className="absolute end-2 top-1/2 -translate-y-1/2 p-1 rounded-full hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
|
||||
aria-label={t('search_clear')}
|
||||
className="absolute end-1 top-1/2 -translate-y-1/2 h-7 w-7 rounded-full text-muted-foreground"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
<X />
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1090,14 +1080,13 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
aria-label={t('search_placeholder')}
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button
|
||||
type="button"
|
||||
<IconButton
|
||||
label={t('search_clear')}
|
||||
onClick={() => setSearchQuery('')}
|
||||
className="absolute end-2 top-1/2 -translate-y-1/2 p-1 rounded-full hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
|
||||
aria-label={t('search_clear')}
|
||||
className="absolute end-1 top-1/2 -translate-y-1/2 h-7 w-7 rounded-full text-muted-foreground"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
<X />
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useTranslations } from "next-intl";
|
||||
import { Plus, Pencil, Trash2, ExternalLink, PanelRight, GripVertical, Lock } from "@/components/icons";
|
||||
import { iconForName } from "@/components/icons";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { IconButton } from "@/components/ui/icon-button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { SettingsSection, SettingItem, ToggleSwitch } from "./settings-section";
|
||||
import { IconPicker } from "@/components/layout/icon-picker";
|
||||
@@ -341,18 +342,16 @@ export function SidebarAppsSettings() {
|
||||
)}>
|
||||
{app.openMode === "inline" ? tApps("inline_badge") : tApps("tab_badge")}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setEditingApp(app.id)}
|
||||
className="p-1.5 rounded-md hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<Pencil className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<button
|
||||
<IconButton label={tApps("edit_app")} onClick={() => setEditingApp(app.id)} className="text-muted-foreground">
|
||||
<Pencil />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
label={tApps("delete")}
|
||||
onClick={() => handleDelete(app)}
|
||||
className="p-1.5 rounded-md hover:bg-destructive/10 text-muted-foreground hover:text-destructive transition-colors"
|
||||
className="text-muted-foreground hover:text-destructive hover:bg-destructive/10"
|
||||
>
|
||||
<Trash2 className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<Trash2 />
|
||||
</IconButton>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "الإعدادات",
|
||||
"back_to_mail": "العودة إلى البريد",
|
||||
"back_to_settings": "العودة إلى الإعدادات",
|
||||
"save_success": "تم حفظ الإعدادات بنجاح",
|
||||
"import_success": "تم استيراد الإعدادات بنجاح",
|
||||
"import_error": "فشل استيراد الإعدادات",
|
||||
@@ -2362,6 +2363,8 @@
|
||||
"add_condition": "إضافة شرط",
|
||||
"actions": "الإجراءات",
|
||||
"add_action": "إضافة إجراء",
|
||||
"remove_condition": "إزالة الشرط",
|
||||
"remove_action": "إزالة الإجراء",
|
||||
"stop_processing": "إيقاف معالجة القواعد التالية",
|
||||
"include_spam": "نقل الرسائل المصنفة كرسائل مزعجة أيضًا",
|
||||
"attachment_type_placeholder": "مثال: pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Configuració",
|
||||
"back_to_mail": "Torna al correu",
|
||||
"back_to_settings": "Torna als paràmetres",
|
||||
"save_success": "Configuració desada correctament",
|
||||
"import_success": "Configuració importada correctament",
|
||||
"import_error": "No s'ha pogut importar la configuració",
|
||||
@@ -2362,6 +2363,8 @@
|
||||
"add_condition": "Afegeix una condició",
|
||||
"actions": "Accions",
|
||||
"add_action": "Afegeix una acció",
|
||||
"remove_condition": "Treu la condició",
|
||||
"remove_action": "Treu l'acció",
|
||||
"stop_processing": "Atura el processament de les regles següents",
|
||||
"include_spam": "Mou també els missatges marcats com a correu brossa",
|
||||
"attachment_type_placeholder": "p. ex. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Nastavení",
|
||||
"back_to_mail": "Zpět na poštu",
|
||||
"back_to_settings": "Zpět do nastavení",
|
||||
"save_success": "Nastavení bylo uloženo",
|
||||
"import_success": "Nastavení bylo importováno",
|
||||
"import_error": "Import nastavení selhal",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "Přidat podmínku",
|
||||
"actions": "Akce",
|
||||
"add_action": "Přidat akci",
|
||||
"remove_condition": "Odebrat podmínku",
|
||||
"remove_action": "Odebrat akci",
|
||||
"stop_processing": "Zastavit zpracování dalších pravidel",
|
||||
"include_spam": "Přesouvat i zprávy označené jako spam",
|
||||
"attachment_type_placeholder": "např. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Indstillinger",
|
||||
"back_to_mail": "Tilbage til mail",
|
||||
"back_to_settings": "Tilbage til indstillinger",
|
||||
"save_success": "Indstillinger gemt succesfuldt",
|
||||
"import_success": "Indstillinger importeret succesfuldt",
|
||||
"import_error": "Kunne ikke importere indstillinger",
|
||||
@@ -2358,6 +2359,8 @@
|
||||
"add_condition": "Tilføj betingelse",
|
||||
"actions": "Handlinger",
|
||||
"add_action": "Tilføj handling",
|
||||
"remove_condition": "Fjern betingelse",
|
||||
"remove_action": "Fjern handling",
|
||||
"stop_processing": "Stop behandling af efterfølgende regler",
|
||||
"include_spam": "Flyt også beskeder, der er markeret som spam",
|
||||
"attachment_type_placeholder": "f.eks. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Einstellungen",
|
||||
"back_to_mail": "Zurück zu E-Mails",
|
||||
"back_to_settings": "Zurück zu den Einstellungen",
|
||||
"save_success": "Einstellungen erfolgreich gespeichert",
|
||||
"import_success": "Einstellungen erfolgreich importiert",
|
||||
"import_error": "Import der Einstellungen fehlgeschlagen",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "Bedingung hinzufügen",
|
||||
"actions": "Aktionen",
|
||||
"add_action": "Aktion hinzufügen",
|
||||
"remove_condition": "Bedingung entfernen",
|
||||
"remove_action": "Aktion entfernen",
|
||||
"stop_processing": "Verarbeitung nachfolgender Regeln stoppen",
|
||||
"include_spam": "Auch als Spam markierte Nachrichten verschieben",
|
||||
"attachment_type_placeholder": "z.B. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"back_to_mail": "Back to Mail",
|
||||
"back_to_settings": "Back to settings",
|
||||
"save_success": "Settings saved successfully",
|
||||
"import_success": "Settings imported",
|
||||
"import_error": "Couldn't import settings",
|
||||
@@ -2362,6 +2363,8 @@
|
||||
"add_condition": "Add Condition",
|
||||
"actions": "Actions",
|
||||
"add_action": "Add Action",
|
||||
"remove_condition": "Remove condition",
|
||||
"remove_action": "Remove action",
|
||||
"stop_processing": "Stop processing subsequent rules",
|
||||
"include_spam": "Also move messages marked as spam",
|
||||
"attachment_type_placeholder": "e.g. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Configuración",
|
||||
"back_to_mail": "Volver al Correo",
|
||||
"back_to_settings": "Volver a los ajustes",
|
||||
"save_success": "Configuración guardada exitosamente",
|
||||
"import_success": "Configuración importada exitosamente",
|
||||
"import_error": "Error al importar la configuración",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "Agregar condición",
|
||||
"actions": "Acciones",
|
||||
"add_action": "Agregar acción",
|
||||
"remove_condition": "Quitar condición",
|
||||
"remove_action": "Quitar acción",
|
||||
"stop_processing": "Detener el procesamiento de reglas posteriores",
|
||||
"include_spam": "Mover también los mensajes marcados como spam",
|
||||
"attachment_type_placeholder": "p. ej. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Seaded",
|
||||
"back_to_mail": "Tagasi postkasti",
|
||||
"back_to_settings": "Tagasi seadete juurde",
|
||||
"save_success": "Seaded salvestatud",
|
||||
"import_success": "Seaded imporditud",
|
||||
"import_error": "Seadete importimine ebaõnnestus",
|
||||
@@ -2362,6 +2363,8 @@
|
||||
"add_condition": "Lisa tingimus",
|
||||
"actions": "Tegevused",
|
||||
"add_action": "Lisa tegevus",
|
||||
"remove_condition": "Eemalda tingimus",
|
||||
"remove_action": "Eemalda toiming",
|
||||
"stop_processing": "Peata järgmiste reeglite töötlemine",
|
||||
"include_spam": "Liiguta ka rämpspostiks märgitud kirjad",
|
||||
"attachment_type_placeholder": "nt pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "تنظیمات",
|
||||
"back_to_mail": "بازگشت به ایمیل",
|
||||
"back_to_settings": "بازگشت به تنظیمات",
|
||||
"save_success": "تنظیمات با موفقیت ذخیره شد",
|
||||
"import_success": "تنظیمات با موفقیت وارد شد",
|
||||
"import_error": "وارد کردن تنظیمات ناموفق بود",
|
||||
@@ -2362,6 +2363,8 @@
|
||||
"add_condition": "افزودن شرط",
|
||||
"actions": "عملیاتها",
|
||||
"add_action": "افزودن عملیات",
|
||||
"remove_condition": "حذف شرط",
|
||||
"remove_action": "حذف کنش",
|
||||
"stop_processing": "توقف پردازش قوانین بعدی",
|
||||
"include_spam": "پیامهای علامتخورده بهعنوان هرزنامه هم منتقل شوند",
|
||||
"attachment_type_placeholder": "مثال: pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Paramètres",
|
||||
"back_to_mail": "Retour aux emails",
|
||||
"back_to_settings": "Retour aux paramètres",
|
||||
"save_success": "Paramètres enregistrés avec succès",
|
||||
"import_success": "Paramètres importés avec succès",
|
||||
"import_error": "Échec de l'importation des paramètres",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "Ajouter une condition",
|
||||
"actions": "Actions",
|
||||
"add_action": "Ajouter une action",
|
||||
"remove_condition": "Retirer la condition",
|
||||
"remove_action": "Retirer l'action",
|
||||
"stop_processing": "Arrêter le traitement des règles suivantes",
|
||||
"include_spam": "Déplacer aussi les messages marqués comme spam",
|
||||
"attachment_type_placeholder": "p. ex. pdf, doc, jpg",
|
||||
|
||||
@@ -1013,6 +1013,7 @@
|
||||
"settings": {
|
||||
"title": "הגדרות",
|
||||
"back_to_mail": "חזרה לדואר",
|
||||
"back_to_settings": "חזרה להגדרות",
|
||||
"save_success": "ההגדרות נשמרו בהצלחה",
|
||||
"import_success": "ההגדרות יובאו בהצלחה",
|
||||
"import_error": "ייבוא ההגדרות נכשל",
|
||||
@@ -2277,6 +2278,8 @@
|
||||
"add_condition": "הוסף מצב",
|
||||
"actions": "פעולות",
|
||||
"add_action": "הוסף פעולה",
|
||||
"remove_condition": "הסרת תנאי",
|
||||
"remove_action": "הסרת פעולה",
|
||||
"stop_processing": "הפסק לעבד את הכללים הבאים",
|
||||
"include_spam": "להעביר גם הודעות שסומנו כדואר זבל",
|
||||
"condition_fields": {
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Beállítások",
|
||||
"back_to_mail": "Vissza a levelekhez",
|
||||
"back_to_settings": "Vissza a beállításokhoz",
|
||||
"save_success": "Beállítások sikeresen elmentve",
|
||||
"import_success": "Beállítások sikeresen importálva",
|
||||
"import_error": "Nem sikerült importálni a beállításokat",
|
||||
@@ -2358,6 +2359,8 @@
|
||||
"add_condition": "Feltétel hozzáadása",
|
||||
"actions": "Műveletek",
|
||||
"add_action": "Művelet hozzáadása",
|
||||
"remove_condition": "Feltétel eltávolítása",
|
||||
"remove_action": "Művelet eltávolítása",
|
||||
"stop_processing": "További szabályok feldolgozásának leállítása",
|
||||
"include_spam": "A levélszemétnek jelölt üzenetek áthelyezése is",
|
||||
"attachment_type_placeholder": "pl. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Impostazioni",
|
||||
"back_to_mail": "Torna alla posta",
|
||||
"back_to_settings": "Torna alle impostazioni",
|
||||
"save_success": "Impostazioni salvate con successo",
|
||||
"import_success": "Impostazioni importate con successo",
|
||||
"import_error": "Impossibile importare le impostazioni",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "Aggiungi condizione",
|
||||
"actions": "Azioni",
|
||||
"add_action": "Aggiungi azione",
|
||||
"remove_condition": "Rimuovi condizione",
|
||||
"remove_action": "Rimuovi azione",
|
||||
"stop_processing": "Interrompere l'elaborazione delle regole successive",
|
||||
"include_spam": "Sposta anche i messaggi contrassegnati come spam",
|
||||
"attachment_type_placeholder": "es. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "設定",
|
||||
"back_to_mail": "メールに戻る",
|
||||
"back_to_settings": "設定に戻る",
|
||||
"save_success": "設定を保存しました",
|
||||
"import_success": "設定をインポートしました",
|
||||
"import_error": "設定のインポートに失敗しました",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "条件を追加",
|
||||
"actions": "アクション",
|
||||
"add_action": "アクションを追加",
|
||||
"remove_condition": "条件を削除",
|
||||
"remove_action": "操作を削除",
|
||||
"stop_processing": "以降のルールの処理を停止",
|
||||
"include_spam": "迷惑メールと判定されたメッセージも移動する",
|
||||
"attachment_type_placeholder": "例: pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "설정",
|
||||
"back_to_mail": "메일로 돌아가기",
|
||||
"back_to_settings": "설정으로 돌아가기",
|
||||
"save_success": "설정이 저장되었어요",
|
||||
"import_success": "설정을 성공적으로 가져왔어요",
|
||||
"import_error": "설정 가져오기에 실패했어요",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "조건 추가",
|
||||
"actions": "동작",
|
||||
"add_action": "동작 추가",
|
||||
"remove_condition": "조건 제거",
|
||||
"remove_action": "동작 제거",
|
||||
"stop_processing": "이후 규칙 무시하기",
|
||||
"include_spam": "스팸으로 표시된 메시지도 이동",
|
||||
"attachment_type_placeholder": "예: pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Iestatījumi",
|
||||
"back_to_mail": "Atgriezties pie pasta",
|
||||
"back_to_settings": "Atpakaļ uz iestatījumiem",
|
||||
"save_success": "Iestatījumi veiksmīgi saglabāti",
|
||||
"import_success": "Iestatījumi veiksmīgi importēti",
|
||||
"import_error": "Neizdevās importēt iestatījumus",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "Pievienot nosacījumu",
|
||||
"actions": "Darbības",
|
||||
"add_action": "Pievienot darbību",
|
||||
"remove_condition": "Noņemt nosacījumu",
|
||||
"remove_action": "Noņemt darbību",
|
||||
"stop_processing": "Pārtraukt nākamo noteikumu apstrādi",
|
||||
"include_spam": "Pārvietot arī ziņas, kas atzīmētas kā mēstules",
|
||||
"attachment_type_placeholder": "piem. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Тохиргоо",
|
||||
"back_to_mail": "Мэйл рүү буцах",
|
||||
"back_to_settings": "Тохиргоо руу буцах",
|
||||
"save_success": "Тохиргоог амжилттай хадгаллаа",
|
||||
"import_success": "Тохиргоог амжилттай импортоллоо",
|
||||
"import_error": "Тохиргоог оруулж чадсангүй",
|
||||
@@ -2362,6 +2363,8 @@
|
||||
"add_condition": "Нөхцөл нэмэх",
|
||||
"actions": "Үйлдэл",
|
||||
"add_action": "Үйлдэл нэмэх",
|
||||
"remove_condition": "Нөхцөлийг хасах",
|
||||
"remove_action": "Үйлдлийг хасах",
|
||||
"stop_processing": "Дараагийн дүрмийг боловсруулахаа зогсоох",
|
||||
"include_spam": "Спам гэж тэмдэглэсэн захидлыг мөн зөөх",
|
||||
"attachment_type_placeholder": "жишээ нь pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Innstillinger",
|
||||
"back_to_mail": "Tilbake til e-post",
|
||||
"back_to_settings": "Tilbake til innstillinger",
|
||||
"save_success": "Innstillingene er lagret",
|
||||
"import_success": "Innstillingene er importert",
|
||||
"import_error": "Kunne ikke importere innstillinger",
|
||||
@@ -2362,6 +2363,8 @@
|
||||
"add_condition": "Legg til vilkår",
|
||||
"actions": "Handlinger",
|
||||
"add_action": "Legg til handling",
|
||||
"remove_condition": "Fjern betingelse",
|
||||
"remove_action": "Fjern handling",
|
||||
"stop_processing": "Ikke behandle flere regler",
|
||||
"include_spam": "Flytt også meldinger som er merket som søppelpost",
|
||||
"attachment_type_placeholder": "f.eks. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Instellingen",
|
||||
"back_to_mail": "Terug naar Mail",
|
||||
"back_to_settings": "Terug naar instellingen",
|
||||
"save_success": "Instellingen succesvol opgeslagen",
|
||||
"import_success": "Instellingen succesvol geïmporteerd",
|
||||
"import_error": "Kan instellingen niet importeren",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "Voorwaarde toevoegen",
|
||||
"actions": "Acties",
|
||||
"add_action": "Actie toevoegen",
|
||||
"remove_condition": "Voorwaarde verwijderen",
|
||||
"remove_action": "Actie verwijderen",
|
||||
"stop_processing": "Verwerking van volgende regels stoppen",
|
||||
"include_spam": "Ook berichten verplaatsen die als spam zijn gemarkeerd",
|
||||
"attachment_type_placeholder": "bijv. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Ustawienia",
|
||||
"back_to_mail": "Powrót do poczty",
|
||||
"back_to_settings": "Wróć do ustawień",
|
||||
"save_success": "Ustawienia zostały zapisane",
|
||||
"import_success": "Ustawienia zostały zaimportowane",
|
||||
"import_error": "Nie udało się zaimportować ustawień",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "Dodaj warunek",
|
||||
"actions": "Akcje",
|
||||
"add_action": "Dodaj akcję",
|
||||
"remove_condition": "Usuń warunek",
|
||||
"remove_action": "Usuń akcję",
|
||||
"stop_processing": "Zatrzymaj przetwarzanie kolejnych reguł",
|
||||
"include_spam": "Przenoś również wiadomości oznaczone jako spam",
|
||||
"attachment_type_placeholder": "np. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Configurações",
|
||||
"back_to_mail": "Voltar para E-mails",
|
||||
"back_to_settings": "Voltar às definições",
|
||||
"save_success": "Configurações salvas com sucesso",
|
||||
"import_success": "Configurações importadas com sucesso",
|
||||
"import_error": "Falha ao importar configurações",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "Adicionar condição",
|
||||
"actions": "Ações",
|
||||
"add_action": "Adicionar ação",
|
||||
"remove_condition": "Remover condição",
|
||||
"remove_action": "Remover ação",
|
||||
"stop_processing": "Parar o processamento das regras seguintes",
|
||||
"include_spam": "Mover também as mensagens marcadas como spam",
|
||||
"attachment_type_placeholder": "ex.: pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Setări",
|
||||
"back_to_mail": "Înapoi la Mail",
|
||||
"back_to_settings": "Înapoi la setări",
|
||||
"save_success": "Setările au fost salvate cu succes",
|
||||
"import_success": "Setările au fost importate cu succes",
|
||||
"import_error": "Nu s-au putut importa setările",
|
||||
@@ -2362,6 +2363,8 @@
|
||||
"add_condition": "Adăugați condiție",
|
||||
"actions": "Acțiuni",
|
||||
"add_action": "Adăugare acțiune",
|
||||
"remove_condition": "Elimină condiția",
|
||||
"remove_action": "Elimină acțiunea",
|
||||
"stop_processing": "Opriți procesarea regulilor următoare",
|
||||
"include_spam": "Mută și mesajele marcate ca spam",
|
||||
"attachment_type_placeholder": "de ex. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Настройки",
|
||||
"back_to_mail": "Вернуться к почте",
|
||||
"back_to_settings": "Назад к настройкам",
|
||||
"save_success": "Настройки успешно сохранены",
|
||||
"import_success": "Настройки успешно импортированы",
|
||||
"import_error": "Не удалось импортировать настройки",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "Добавить условие",
|
||||
"actions": "Действия",
|
||||
"add_action": "Добавить действие",
|
||||
"remove_condition": "Убрать условие",
|
||||
"remove_action": "Убрать действие",
|
||||
"stop_processing": "Прекратить обработку последующих правил",
|
||||
"include_spam": "Перемещать также письма, помеченные как спам",
|
||||
"attachment_type_placeholder": "напр. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Nastavenia",
|
||||
"back_to_mail": "Späť na poštu",
|
||||
"back_to_settings": "Späť do nastavení",
|
||||
"save_success": "Nastavenia boli úspešne uložené",
|
||||
"import_success": "Nastavenia boli úspešne importované",
|
||||
"import_error": "Import nastavení zlyhal",
|
||||
@@ -2362,6 +2363,8 @@
|
||||
"add_condition": "Pridať podmienku",
|
||||
"actions": "Akcie",
|
||||
"add_action": "Pridať akciu",
|
||||
"remove_condition": "Odobrať podmienku",
|
||||
"remove_action": "Odobrať akciu",
|
||||
"stop_processing": "Zastaviť spracovanie ďalších pravidiel",
|
||||
"include_spam": "Presúvať aj správy označené ako spam",
|
||||
"attachment_type_placeholder": "napr. pdf, doc, jpg",
|
||||
|
||||
@@ -3058,6 +3058,7 @@
|
||||
"settings": {
|
||||
"title": "การตั้งค่า",
|
||||
"back_to_mail": "กลับไปยังอีเมล",
|
||||
"back_to_settings": "กลับไปที่การตั้งค่า",
|
||||
"save_success": "บันทึกการตั้งค่าเรียบร้อยแล้ว",
|
||||
"import_success": "นำเข้าการตั้งค่าเรียบร้อยแล้ว",
|
||||
"import_error": "นำเข้าการตั้งค่าไม่สำเร็จ",
|
||||
@@ -4372,6 +4373,8 @@
|
||||
"add_condition": "เพิ่มเงื่อนไข",
|
||||
"actions": "การดำเนินการ",
|
||||
"add_action": "เพิ่มการดำเนินการ",
|
||||
"remove_condition": "นำเงื่อนไขออก",
|
||||
"remove_action": "นำการทำงานออก",
|
||||
"stop_processing": "หยุดการประมวลผลกฎข้อถัดไป",
|
||||
"include_spam": "ย้ายข้อความที่ทำเครื่องหมายว่าเป็นสแปมด้วย",
|
||||
"attachment_type_placeholder": "เช่น pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Ayarlar",
|
||||
"back_to_mail": "Postaya Dön",
|
||||
"back_to_settings": "Ayarlara dön",
|
||||
"save_success": "Ayarlar başarıyla kaydedildi",
|
||||
"import_success": "Ayarlar başarıyla içe aktarıldı",
|
||||
"import_error": "Ayarlar içe aktarılamadı",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "Koşul Ekle",
|
||||
"actions": "İşlemler",
|
||||
"add_action": "İşlem Ekle",
|
||||
"remove_condition": "Koşulu kaldır",
|
||||
"remove_action": "Eylemi kaldır",
|
||||
"stop_processing": "Sonraki kuralları işlemeyi durdur",
|
||||
"include_spam": "İstenmeyen olarak işaretlenen iletileri de taşı",
|
||||
"attachment_type_placeholder": "örn. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "Налаштування",
|
||||
"back_to_mail": "Назад до пошти",
|
||||
"back_to_settings": "Назад до налаштувань",
|
||||
"save_success": "Налаштування успішно збережено",
|
||||
"import_success": "Налаштування успішно імпортовано",
|
||||
"import_error": "Не вдалося імпортувати налаштування",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "Додати умову",
|
||||
"actions": "Дії",
|
||||
"add_action": "Додати дію",
|
||||
"remove_condition": "Прибрати умову",
|
||||
"remove_action": "Прибрати дію",
|
||||
"stop_processing": "Зупинити обробку наступних правил",
|
||||
"include_spam": "Переміщувати також листи, позначені як спам",
|
||||
"attachment_type_placeholder": "напр. pdf, doc, jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "設定",
|
||||
"back_to_mail": "回到郵件",
|
||||
"back_to_settings": "返回設定",
|
||||
"save_success": "設定已成功儲存",
|
||||
"import_success": "已成功匯入設定",
|
||||
"import_error": "無法匯入設定",
|
||||
@@ -2362,6 +2363,8 @@
|
||||
"add_condition": "新增條件",
|
||||
"actions": "動作",
|
||||
"add_action": "新增動作",
|
||||
"remove_condition": "移除條件",
|
||||
"remove_action": "移除動作",
|
||||
"stop_processing": "停止處理後續規則",
|
||||
"include_spam": "同時移動被標記為垃圾郵件的郵件",
|
||||
"attachment_type_placeholder": "例如:pdf、doc、jpg",
|
||||
|
||||
@@ -1048,6 +1048,7 @@
|
||||
"settings": {
|
||||
"title": "设置",
|
||||
"back_to_mail": "返回邮件",
|
||||
"back_to_settings": "返回设置",
|
||||
"save_success": "设置保存成功",
|
||||
"import_success": "设置导入成功",
|
||||
"import_error": "导入设置失败",
|
||||
@@ -2355,6 +2356,8 @@
|
||||
"add_condition": "添加条件",
|
||||
"actions": "操作",
|
||||
"add_action": "添加操作",
|
||||
"remove_condition": "移除条件",
|
||||
"remove_action": "移除操作",
|
||||
"stop_processing": "停止处理后续规则",
|
||||
"include_spam": "同时移动被标记为垃圾邮件的邮件",
|
||||
"attachment_type_placeholder": "例如:pdf、doc、jpg",
|
||||
|
||||
Reference in New Issue
Block a user