mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-05 08:16:22 +00:00
Merge branch 'feat/do-uxs-structure' into feat/do-ux-settings-fixes
# Conflicts: # lib/settings-search.ts
This commit is contained in:
@@ -208,7 +208,10 @@
|
||||
[data-boot-settings-tab="contacts"] [data-sk-settings-tab="contacts"],
|
||||
[data-boot-settings-tab="files"] [data-sk-settings-tab="files"],
|
||||
[data-boot-settings-tab="addons"] [data-sk-settings-tab="addons"],
|
||||
[data-boot-settings-tab="backup"] [data-sk-settings-tab="backup"]
|
||||
[data-boot-settings-tab="offline"] [data-sk-settings-tab="offline"],
|
||||
[data-boot-settings-tab="privacy"] [data-sk-settings-tab="privacy"],
|
||||
[data-boot-settings-tab="backup"] [data-sk-settings-tab="backup"],
|
||||
[data-boot-settings-tab="about"] [data-sk-settings-tab="about"]
|
||||
));
|
||||
|
||||
/* Settings pages the nav listed last time (bulwark-boot-hints). Without
|
||||
@@ -221,6 +224,7 @@
|
||||
@custom-variant boot-no-settings-contacts (&:where([data-boot-settings-tabs]:not([data-boot-settings-tabs~="contacts"]) *));
|
||||
@custom-variant boot-no-settings-files (&:where([data-boot-settings-tabs]:not([data-boot-settings-tabs~="files"]) *));
|
||||
@custom-variant boot-no-settings-addons (&:where([data-boot-settings-tabs]:not([data-boot-settings-tabs~="addons"]) *));
|
||||
@custom-variant boot-no-settings-offline (&:where([data-boot-settings-tabs]:not([data-boot-settings-tabs~="offline"]) *));
|
||||
@custom-variant boot-settings-recovery (&:where([data-boot-settings-tabs~="recovery"] *));
|
||||
|
||||
/* Settings pages with one section have no section jump row (SectionNav). */
|
||||
|
||||
@@ -55,7 +55,7 @@ export function OfflineSection({ draft }: { draft: OrgPolicyDraft }) {
|
||||
<SettingsSection
|
||||
id="offline"
|
||||
title="Offline mode"
|
||||
description="People can keep mail on a device to read and write it without a connection. In browsers it is off until someone turns on “Keep mail on this device” in Settings > Backup & reset. In the Bulwark desktop app it is on unless you forbid it here."
|
||||
description="People can keep mail on a device to read and write it without a connection. In browsers it is off until someone turns on “Keep mail on this device” in Settings > Offline. In the Bulwark desktop app it is on unless you forbid it here."
|
||||
>
|
||||
<SettingItem
|
||||
label="Don't let people keep mail on their devices"
|
||||
|
||||
@@ -28,6 +28,8 @@ const OPTIONAL: Partial<Record<SettingsSearchTab, string>> = {
|
||||
contacts: "boot-no-settings-contacts:hidden",
|
||||
files: "boot-no-files:hidden boot-no-settings-files:hidden",
|
||||
addons: "boot-no-settings-addons:hidden",
|
||||
// Not in the static Lite build.
|
||||
offline: "boot-no-settings-offline:hidden",
|
||||
};
|
||||
|
||||
/** A line of text as tall as its line box, so rows keep the real height. */
|
||||
|
||||
@@ -31,7 +31,8 @@ vi.stubGlobal('ResizeObserver', class { observe() {} unobserve() {} disconnect()
|
||||
|
||||
const { EmailViewer } = await import('../email-viewer');
|
||||
const { ThreadConversationView } = await import('../thread-conversation-view');
|
||||
const { ReadingSettings } = await import('@/components/settings/reading-settings');
|
||||
// Remote content and trusted senders moved from Reading to Privacy (settings-24, 2026-11-04).
|
||||
const { PrivacySettings } = await import('@/components/settings/privacy-settings');
|
||||
|
||||
const mailboxes = [{ id: 'inbox', name: 'Inbox', role: 'inbox', parentId: null, myRights: { mayAddItems: true } }] as unknown as Mailbox[];
|
||||
const email = {
|
||||
@@ -123,7 +124,7 @@ describe('before the policy has loaded', () => {
|
||||
|
||||
describe('trusted senders settings', () => {
|
||||
it('lets users add senders and switch the address book when nothing blocks', async () => {
|
||||
render(<ReadingSettings />);
|
||||
render(<PrivacySettings />);
|
||||
const toggle = screen.getByText('trusted_senders.use_address_book_label').closest('[class]')!.parentElement!;
|
||||
expect(toggle.querySelector('[inert]')).toBeNull();
|
||||
fireEvent.click(screen.getByText('trusted_senders.count_zero'));
|
||||
@@ -132,7 +133,7 @@ describe('trusted senders settings', () => {
|
||||
|
||||
it('shows the list read-only and the address book switch as the organisation\'s while it blocks', async () => {
|
||||
restrict({ locked: true, value: 'block' });
|
||||
render(<ReadingSettings />);
|
||||
render(<PrivacySettings />);
|
||||
expect(screen.getAllByText('trusted_senders_note').length).toBeGreaterThan(0);
|
||||
const switchRow = screen.getByText('trusted_senders.use_address_book_label').closest('div')!.parentElement!.parentElement!;
|
||||
expect(switchRow.querySelector('[inert]')).not.toBeNull();
|
||||
@@ -145,7 +146,7 @@ describe('trusted senders settings', () => {
|
||||
it('still lets users take a sender off the list while it blocks', async () => {
|
||||
restrict({ locked: true, value: 'block' });
|
||||
useSettingsStore.setState({ trustedSenders: ['news@shop.example'], trustedSendersAddressBook: false });
|
||||
render(<ReadingSettings />);
|
||||
render(<PrivacySettings />);
|
||||
fireEvent.click(screen.getByText('trusted_senders.count_one'));
|
||||
await act(async () => { await new Promise((r) => setTimeout(r, 10)); });
|
||||
fireEvent.click(screen.getByTitle('remove'));
|
||||
|
||||
@@ -57,7 +57,7 @@ async function freeRoom(): Promise<number | null> {
|
||||
}
|
||||
}
|
||||
|
||||
/** Settings > Backup & reset > Offline > Export kept mail: from the local store, also with no server. */
|
||||
/** Settings > Offline > Export kept mail: from the local store, also with no server. */
|
||||
export function LocalExportDialog({ request }: { request: LocalExportRequest }) {
|
||||
const t = useTranslations("offlineExport");
|
||||
const tExport = useTranslations("mail_export");
|
||||
|
||||
@@ -168,6 +168,16 @@ describe('what it takes', () => {
|
||||
expect(line.querySelector('progress')?.getAttribute('max')).toBe('5000');
|
||||
});
|
||||
|
||||
// settings-26 (2026-11-04): "Saving your mail on this device…" stood above
|
||||
// the progress line, which said the same thing again.
|
||||
it('says a large sync is running once, in the progress line', () => {
|
||||
const now = Date.now();
|
||||
useOfflineStore.setState({ syncing: ['acc'], progress: { acc: { area: 'mail', phase: 'bodies', done: 500, total: 5000, startedAt: now, startedDone: 0, at: now } } });
|
||||
render(<OfflineSettings />);
|
||||
expect(screen.getByTestId('offline-progress')).toBeTruthy();
|
||||
expect(document.body.textContent).not.toContain('syncing');
|
||||
});
|
||||
|
||||
it('keeps a small catch-up quiet', () => {
|
||||
const now = Date.now();
|
||||
useOfflineStore.setState({ syncing: ['acc'], progress: { acc: { area: 'mail', phase: 'bodies', done: 1, total: 3, startedAt: now, startedDone: 0, at: now } } });
|
||||
|
||||
@@ -63,7 +63,7 @@ function useUnsent(account: string) {
|
||||
return { unsent, read };
|
||||
}
|
||||
|
||||
/** One account in Settings > Backup & reset > Offline. */
|
||||
/** One account in Settings > Offline. */
|
||||
export function AccountStorage({ account, primary, heading }: { account: string; primary: boolean; heading: string | null }) {
|
||||
const t = useTranslations('offline.settings');
|
||||
const tStorage = useTranslations('offlineStorage');
|
||||
@@ -164,14 +164,18 @@ export function AccountStorage({ account, primary, heading }: { account: string;
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-2 py-3">
|
||||
<p className="text-xs text-muted-foreground" role="status" aria-live="polite" data-testid={testId('offline-status')}>
|
||||
{syncing
|
||||
? t('syncing')
|
||||
: lastSynced
|
||||
? t('usage_updated', { size: formatFileSize(usage?.bytes ?? 0), time: format.relativeTime(lastSynced, now) })
|
||||
: t('usage', { size: formatFileSize(usage?.bytes ?? 0) })}
|
||||
</p>
|
||||
{syncing && progressWorthShowing(progress) && <ProgressLine progress={progress} />}
|
||||
{/* A sync with a progress line says so once, in that line. */}
|
||||
{syncing && progressWorthShowing(progress) ? (
|
||||
<ProgressLine progress={progress} />
|
||||
) : (
|
||||
<p className="text-xs text-muted-foreground" role="status" aria-live="polite" data-testid={testId('offline-status')}>
|
||||
{syncing
|
||||
? t('syncing')
|
||||
: lastSynced
|
||||
? t('usage_updated', { size: formatFileSize(usage?.bytes ?? 0), time: format.relativeTime(lastSynced, now) })
|
||||
: t('usage', { size: formatFileSize(usage?.bytes ?? 0) })}
|
||||
</p>
|
||||
)}
|
||||
{usage && usage.parts.length > 0 && (
|
||||
<dl className="grid grid-cols-[1fr_auto] gap-x-4 gap-y-1 text-xs" aria-label={tStorage('usage_label')} data-testid={testId('offline-usage')}>
|
||||
{usage.parts.filter((part) => part.items > 0).map((part) => (
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { formatFileSize } from '@/lib/utils';
|
||||
import { useKeptFiles } from '@/hooks/use-kept-files';
|
||||
|
||||
/** The files this account keeps until told otherwise, in Settings > Backup & reset > Offline. */
|
||||
/** The files this account keeps until told otherwise, in Settings > Offline. */
|
||||
export function KeptFiles({ account }: { account: string }) {
|
||||
const t = useTranslations('offlineStorage');
|
||||
const { pinned, unpin } = useKeptFiles(account);
|
||||
|
||||
@@ -43,7 +43,7 @@ export function OfflineHost() {
|
||||
useEffect(() => {
|
||||
const show = (notice: OfflineNotice) => {
|
||||
const tr = tRef.current;
|
||||
const openStorage = { label: tStorageRef.current('open_settings'), onClick: () => routerRef.current.push(buildSettingsPath('backup', 'offline')) };
|
||||
const openStorage = { label: tStorageRef.current('open_settings'), onClick: () => routerRef.current.push(buildSettingsPath('offline', 'offline')) };
|
||||
switch (notice.kind) {
|
||||
case 'queued':
|
||||
toast.info(tr('toast.queued'));
|
||||
|
||||
@@ -19,13 +19,14 @@ import { KeyProtectionNotes } from './key-protection-notes';
|
||||
import { WipeDeviceSection } from './wipe-device-section';
|
||||
|
||||
/**
|
||||
* Settings > Backup & reset > Offline (#523): for each account signed in here,
|
||||
* whether it keeps mail on this device, how much, what that takes, and the way
|
||||
* to clear it. Above that what the browser may do to it, below it the outbox
|
||||
* and whatever didn't go through. Not in Lite.
|
||||
* Settings > Offline (#523), a page of its own: for each account signed in
|
||||
* here, whether it keeps mail on this device, how much, what that takes, and
|
||||
* the way to clear it. Above that what the browser may do to it; in a section
|
||||
* below it the outbox and whatever didn't go through. Not in Lite.
|
||||
*/
|
||||
export function OfflineSettings() {
|
||||
const t = useTranslations('offline.settings');
|
||||
const tOutbox = useTranslations('offline.outbox');
|
||||
const tStorage = useTranslations('offlineStorage');
|
||||
const active = useAuthStore((s) => s.activeAccountId);
|
||||
useAuthStore((s) => s.connectedAccountsRevision);
|
||||
@@ -55,39 +56,43 @@ export function OfflineSettings() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingsSection id="offline" title={t('title')} description={t('description')}>
|
||||
{blocked ? (
|
||||
<p className="text-sm text-muted-foreground" data-testid="offline-private">{tNotes('store_private')}</p>
|
||||
) : !offlineSupported() ? (
|
||||
<p className="text-sm text-muted-foreground" data-testid="offline-unsupported">{t('unsupported')}</p>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-start gap-2 rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs text-amber-900 dark:text-amber-200">
|
||||
<AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0" aria-hidden="true" />
|
||||
<p data-testid="offline-warning">{isDesktopApp() ? tStorage('warning_desktop') : t('warning')}</p>
|
||||
</div>
|
||||
<KeyProtectionNotes browser />
|
||||
{enabledCount > 0 && persist === 'refused' && (
|
||||
<div className="flex flex-col gap-2 rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs text-amber-900 dark:text-amber-200 sm:flex-row sm:items-center sm:justify-between" role="status" data-testid="offline-persist-refused">
|
||||
<p className="min-w-0">{tStorage('persist_refused')}</p>
|
||||
<Button variant="outline" size="sm" className="shrink-0" disabled={asking} onClick={() => void askAgain()} data-testid="offline-persist-retry">{tStorage('persist_retry')}</Button>
|
||||
<div className="space-y-8">
|
||||
<SettingsSection id="offline" title={t('title')} description={t('description')}>
|
||||
{blocked ? (
|
||||
<p className="text-sm text-muted-foreground" data-testid="offline-private">{tNotes('store_private')}</p>
|
||||
) : !offlineSupported() ? (
|
||||
<p className="text-sm text-muted-foreground" data-testid="offline-unsupported">{t('unsupported')}</p>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-start gap-2 rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs text-amber-900 dark:text-amber-200">
|
||||
<AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0" aria-hidden="true" />
|
||||
<p data-testid="offline-warning">{isDesktopApp() ? tStorage('warning_desktop') : t('warning')}</p>
|
||||
</div>
|
||||
)}
|
||||
{enabledCount > 0 && safariMayDelete() && (
|
||||
<p className="rounded-md border border-border bg-muted px-3 py-2 text-xs text-foreground" data-testid="offline-safari-hint">{tStorage('safari_hint')}</p>
|
||||
)}
|
||||
{accounts.map((account, index) => (
|
||||
<AccountStorage
|
||||
key={account}
|
||||
account={account}
|
||||
primary={index === 0}
|
||||
heading={several ? labels.find((entry) => entry.id === account)?.email || labels.find((entry) => entry.id === account)?.label || account : null}
|
||||
/>
|
||||
))}
|
||||
{[active, ...accounts].some((account) => offlineAllowed(account)) && <WipeDeviceSection />}
|
||||
</>
|
||||
)}
|
||||
<OutboxList />
|
||||
</SettingsSection>
|
||||
<KeyProtectionNotes browser />
|
||||
{enabledCount > 0 && persist === 'refused' && (
|
||||
<div className="flex flex-col gap-2 rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs text-amber-900 dark:text-amber-200 sm:flex-row sm:items-center sm:justify-between" role="status" data-testid="offline-persist-refused">
|
||||
<p className="min-w-0">{tStorage('persist_refused')}</p>
|
||||
<Button variant="outline" size="sm" className="shrink-0" disabled={asking} onClick={() => void askAgain()} data-testid="offline-persist-retry">{tStorage('persist_retry')}</Button>
|
||||
</div>
|
||||
)}
|
||||
{enabledCount > 0 && safariMayDelete() && (
|
||||
<p className="rounded-md border border-border bg-muted px-3 py-2 text-xs text-foreground" data-testid="offline-safari-hint">{tStorage('safari_hint')}</p>
|
||||
)}
|
||||
{accounts.map((account, index) => (
|
||||
<AccountStorage
|
||||
key={account}
|
||||
account={account}
|
||||
primary={index === 0}
|
||||
heading={several ? labels.find((entry) => entry.id === account)?.email || labels.find((entry) => entry.id === account)?.label || account : null}
|
||||
/>
|
||||
))}
|
||||
{[active, ...accounts].some((account) => offlineAllowed(account)) && <WipeDeviceSection />}
|
||||
</>
|
||||
)}
|
||||
</SettingsSection>
|
||||
<SettingsSection id="outbox" title={tOutbox('title')}>
|
||||
<OutboxList heading={false} />
|
||||
</SettingsSection>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export function WipeDeviceSection() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap items-center justify-between gap-3 border-t border-border py-3" data-testid="wipe-device-section">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3 border-t border-border py-3" data-testid="wipe-device-section" data-search-label={t('settings_title')}>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm font-medium text-foreground">{t('settings_title')}</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">{t('settings_description')}</p>
|
||||
|
||||
@@ -154,3 +154,49 @@ describe('read aloud voice', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// settings-10 (2026-11-04): Accessibility repeated about twenty settings of
|
||||
// other pages, two rows of it called "Notification sound". Only access
|
||||
// settings stay; a link row in each section leads to the page with the rest.
|
||||
describe('one setting, one place', () => {
|
||||
const borrowed = [
|
||||
'settings.appearance.list_density.label',
|
||||
'settings.appearance.toolbar_labels.label',
|
||||
'settings.email_behavior.always_light_mode.label',
|
||||
'calendar.settings.hover_preview',
|
||||
'settings.email_behavior.group_conversations.label',
|
||||
'settings.email_behavior.show_preview.label',
|
||||
'settings.advanced.sender_favicons.label',
|
||||
'settings.email_behavior.mark_read.label',
|
||||
'settings.email_behavior.return_to_list_after_action.label',
|
||||
'settings.email_behavior.send_delay.label',
|
||||
'settings.email_behavior.confirm_bulk_delete.label',
|
||||
'settings.notifications.email.sound',
|
||||
'settings.notifications.calendar.sound',
|
||||
'settings.notifications.sound_selection.choose',
|
||||
'settings.appearance.favicon_unread_badge.label',
|
||||
];
|
||||
|
||||
it('draws no row that belongs to another page', () => {
|
||||
const { container } = render(<AccessibilitySettings />);
|
||||
const rows = Array.from(container.querySelectorAll('[data-search-label]')).map((el) => el.getAttribute('data-search-label'));
|
||||
expect(rows.filter((row) => borrowed.includes(row ?? ''))).toEqual([]);
|
||||
});
|
||||
|
||||
it('links to the pages that have them', () => {
|
||||
const goTo: unknown[] = [];
|
||||
const listener = (e: Event) => goTo.push((e as CustomEvent).detail);
|
||||
window.addEventListener('settings-tab-change', listener);
|
||||
try {
|
||||
render(<AccessibilitySettings />);
|
||||
for (const link of ['text', 'light_mode', 'list', 'reading', 'bulk_delete', 'sounds']) {
|
||||
expect(screen.getByText(`uxs_structure.a11y_links.${link}.label`)).toBeTruthy();
|
||||
}
|
||||
const soundsRow = screen.getByText('uxs_structure.a11y_links.sounds.label').closest('[data-search-label]') as HTMLElement;
|
||||
fireEvent.click(within(soundsRow).getByRole('button'));
|
||||
expect(goTo).toEqual([{ tab: 'notifications', section: 'sound' }]);
|
||||
} finally {
|
||||
window.removeEventListener('settings-tab-change', listener);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { afterEach, describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { MessageListSettings } from '../message-list-settings';
|
||||
import { NotificationSettings } from '../notification-settings';
|
||||
import { PluginsSettings } from '../plugins-settings';
|
||||
import { ReadingSettings } from '../reading-settings';
|
||||
import { PrivacySettings } from '../privacy-settings';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { usePolicyStore } from '@/stores/policy-store';
|
||||
import { usePluginStore } from '@/stores/plugin-store';
|
||||
@@ -13,7 +13,11 @@ import type { InstalledPlugin } from '@/lib/plugin-types';
|
||||
|
||||
// #903: the settings pages hide or explain what the no-third-party mode turns off.
|
||||
|
||||
vi.mock('next-intl', () => ({ useTranslations: () => (key: string) => key, useLocale: () => 'en' }));
|
||||
vi.mock('next-intl', () => ({
|
||||
useTranslations: () => (key: string) => key,
|
||||
useLocale: () => 'en',
|
||||
useFormatter: () => ({ dateTime: String, relativeTime: String, number: String }),
|
||||
}));
|
||||
vi.mock('@/hooks/use-config', () => ({ useConfig: () => ({ demoMode: false }) }));
|
||||
vi.mock('../settings-section', async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import('../settings-section')>()),
|
||||
@@ -46,14 +50,22 @@ beforeEach(() => {
|
||||
});
|
||||
afterEach(() => setNoThirdPartyTraffic(false));
|
||||
|
||||
describe('Settings > Message list', () => {
|
||||
// Sender pictures, remote content and trusted senders moved from Message
|
||||
// list and Reading to Privacy (settings-24, 2026-11-04).
|
||||
describe('Settings > Privacy', () => {
|
||||
it('offers sender favicons only while third-party traffic is allowed', () => {
|
||||
const { unmount } = render(<MessageListSettings />);
|
||||
const { unmount } = render(<PrivacySettings />);
|
||||
expect(screen.getByText('sender_favicons.label')).toBeInTheDocument();
|
||||
unmount();
|
||||
setNoThirdPartyTraffic(true);
|
||||
render(<PrivacySettings />);
|
||||
expect(screen.queryByText('sender_favicons.label')).toBeNull();
|
||||
});
|
||||
|
||||
it('leaves a link to Privacy on Message list', () => {
|
||||
render(<MessageListSettings />);
|
||||
expect(screen.queryByText('sender_favicons.label')).toBeNull();
|
||||
expect(screen.getByText('senders_label')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,14 +103,14 @@ describe('Settings > Plugins', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Settings > Reading', () => {
|
||||
describe('Settings > Privacy: remote content', () => {
|
||||
function externalContentRow() {
|
||||
return screen.getByText('external_content.label').closest('[data-search-label]') as HTMLElement;
|
||||
}
|
||||
|
||||
it('lets the user pick the external content policy without the mode', () => {
|
||||
useSettingsStore.setState({ externalContentPolicy: 'ask' });
|
||||
render(<ReadingSettings />);
|
||||
render(<PrivacySettings />);
|
||||
expect(externalContentRow().textContent).not.toContain('org_locked');
|
||||
expect(externalContentRow().querySelector('select')?.value).toBe('ask');
|
||||
});
|
||||
@@ -106,7 +118,7 @@ describe('Settings > Reading', () => {
|
||||
it('shows the policy as blocked and set by the organisation in the mode', () => {
|
||||
setNoThirdPartyTraffic(true);
|
||||
useSettingsStore.setState({ externalContentPolicy: 'allow' });
|
||||
render(<ReadingSettings />);
|
||||
render(<PrivacySettings />);
|
||||
const row = externalContentRow();
|
||||
expect(row.textContent).toContain('org_locked');
|
||||
expect(row.querySelector('select')?.value).toBe('block');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'fake-indexeddb/auto';
|
||||
import { render } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { NextIntlClientProvider } from 'next-intl';
|
||||
@@ -8,6 +9,8 @@ import en from '@/locales/en/common.json';
|
||||
// off and in the situations some rows need (touch screen, Mountain View,
|
||||
// several accounts), and checks that each entry of the search index has a
|
||||
// row marked with its label, and is advanced exactly when the page says so.
|
||||
// settings-8 (2026-11-04): and that every section a page draws can be found,
|
||||
// so a new section (offline, app lock) can't be left out of the index.
|
||||
|
||||
vi.unmock('next-intl');
|
||||
vi.mock('@/components/plugins/plugin-iframe-slot', () => ({ PluginIframeSlot: () => null }));
|
||||
@@ -33,7 +36,8 @@ const { useEmailStore } = await import('@/stores/email-store');
|
||||
const { useAccountStore } = await import('@/stores/account-store');
|
||||
const { TourProvider } = await import('@/components/tour/tour-provider');
|
||||
const { AdvancedSettingsProvider } = await import('../settings-section');
|
||||
const { tabSearchEntries, getByPath } = await import('@/lib/settings-search');
|
||||
const { tabSearchEntries, getByPath, withAppName, settingsTabLabelKey } = await import('@/lib/settings-search');
|
||||
const { setStoreBackend } = await import('@/lib/store/client/api');
|
||||
const pages = {
|
||||
account: (await import('../account-settings')).AccountSettings,
|
||||
general: (await import('../general-settings')).GeneralSettings,
|
||||
@@ -56,6 +60,9 @@ const pages = {
|
||||
files: (await import('../files-settings')).FilesSettingsComponent,
|
||||
addons: (await import('../addons-settings')).AddonsSettings,
|
||||
backup: (await import('../about-data-settings')).AboutDataSettings,
|
||||
offline: (await import('@/components/offline/offline-settings')).OfflineSettings,
|
||||
privacy: (await import('../privacy-settings')).PrivacySettings,
|
||||
about: (await import('../about-data-settings')).AboutSettings,
|
||||
};
|
||||
|
||||
const OVERRIDES: Record<string, () => unknown> = {
|
||||
@@ -77,19 +84,22 @@ const TAB_PAGES: Record<string, string[]> = {
|
||||
message_list: ['message_list'], reading: ['reading'], composing: ['composing'], vacation: ['vacation'],
|
||||
filters: ['filters'], organize: ['organize'], security: ['security'], sharing: ['sharing'], recovery: ['recovery'], calendar: ['calendar', 'calendar2'],
|
||||
contacts: ['contacts', 'contacts2'], files: ['files'], addons: ['addons'], backup: ['backup'],
|
||||
offline: ['offline'], privacy: ['privacy'], about: ['about'],
|
||||
};
|
||||
|
||||
describe('settings search index', () => {
|
||||
it('has a row for every entry on its page', { timeout: 120_000 }, () => {
|
||||
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('no network')));
|
||||
useAuthStore.setState({ isAuthenticated: true, authMode: 'basic', client: CLIENT } as never);
|
||||
useAuthStore.setState({ isAuthenticated: true, authMode: 'basic', client: CLIENT, activeAccountId: 'acc' } as never);
|
||||
useVacationStore.setState({ isSupported: true, isLoading: false, forwardAvailable: true, fetchVacationResponse: vi.fn(async () => {}) } as never);
|
||||
useFilterStore.setState({ isSupported: true, isLoading: false, rules: [], selectAccount: vi.fn(async () => {}) } as never);
|
||||
useAccountSecurityStore.setState({ isStalwart: true, isProbing: false, probe: vi.fn(async () => true), fetchAll: vi.fn(), fetchAuthInfo: vi.fn(), fetchPublicKeys: vi.fn(), fetchCryptoInfo: vi.fn() } as never);
|
||||
useCalendarStore.setState({ calendars: [{ id: 'c', name: 'Work', color: '#3b82f6', isDefault: false, isShared: false, shareWith: null, myRights: { mayShare: true, mayDelete: true, mayWriteAll: true } }], icalSubscriptions: [] } as never);
|
||||
useContactStore.setState({ addressBooks: [{ id: 'b', name: 'Club', isDefault: true, isShared: false, shareWith: null, myRights: { mayRead: true, mayWrite: true, mayDelete: true } }], contacts: [], fetchAddressBooks: vi.fn(async () => {}) } as never);
|
||||
useEmailStore.setState({ fetchMailboxes: vi.fn(async () => {}) } as never);
|
||||
const out: Record<string, Record<string, { on: string[]; off: string[]; error?: string }>> = {};
|
||||
// jsdom has no OPFS: the store says it's there, as a browser would, so Offline draws its rows.
|
||||
setStoreBackend({ supported: () => true } as never);
|
||||
const out: Record<string, Record<string, { on: string[]; off: string[]; sections: string[]; error?: string }>> = {};
|
||||
const realMatchMedia = window.matchMedia;
|
||||
const variants: Record<string, () => void> = {
|
||||
base: () => {},
|
||||
@@ -125,13 +135,16 @@ describe('settings search index', () => {
|
||||
</NextIntlClientProvider>,
|
||||
);
|
||||
const found = Array.from(container.querySelectorAll('[data-search-label]')).map((el) => el.getAttribute('data-search-label') ?? '');
|
||||
const sections = Array.from(container.querySelectorAll('[data-settings-section]')).map((el) => el.getAttribute('data-search-label') ?? '');
|
||||
unmount();
|
||||
return found;
|
||||
return { found, sections };
|
||||
};
|
||||
try {
|
||||
out[variant][tab] = { on: labels(true), off: labels(false) };
|
||||
const on = labels(true);
|
||||
const off = labels(false);
|
||||
out[variant][tab] = { on: on.found, off: off.found, sections: on.sections };
|
||||
} catch (e) {
|
||||
out[variant][tab] = { on: [], off: [], error: String(e) };
|
||||
out[variant][tab] = { on: [], off: [], sections: [], error: String(e) };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +153,7 @@ describe('settings search index', () => {
|
||||
const problems: string[] = [];
|
||||
for (const [tab, entries] of Object.entries(tabSearchEntries)) {
|
||||
for (const entry of entries) {
|
||||
const text = getByPath(en, entry.label) as string;
|
||||
const text = withAppName(getByPath(en, entry.label), 'Bulwark') as string;
|
||||
const variant = out[entry.when ?? 'base'];
|
||||
const parts = TAB_PAGES[tab].map((page) => variant[page]);
|
||||
const errors = parts.filter((part) => part.error).map((part) => part.error);
|
||||
@@ -153,6 +166,17 @@ describe('settings search index', () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Every section a page draws has an entry, so searching its name finds it.
|
||||
for (const [tab, entries] of Object.entries(tabSearchEntries)) {
|
||||
const indexed = new Set(entries.map((entry) => withAppName(getByPath(en, entry.label), 'Bulwark') as string));
|
||||
// A section named like its page is the page itself, which the search lists anyway.
|
||||
indexed.add(getByPath(en, settingsTabLabelKey(tab as never)) as string);
|
||||
for (const page of TAB_PAGES[tab]) {
|
||||
for (const section of out.base[page].sections) {
|
||||
if (!indexed.has(section)) problems.push(`${tab}: section "${section}" has no search entry`);
|
||||
}
|
||||
}
|
||||
}
|
||||
expect([...new Set(problems)]).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,6 +25,24 @@ vi.mock('@/components/plugins/app-top-banner-slot', () => ({ AppTopBannerSlot: (
|
||||
vi.mock('@/components/settings/about-data-settings', () => ({
|
||||
AboutDataSettings: () => null,
|
||||
AboutFooter: () => <div data-testid="about-footer" />,
|
||||
AboutSettings: () => <div data-testid="about-footer" />,
|
||||
}));
|
||||
vi.mock('@/components/offline/offline-settings', () => ({
|
||||
OfflineSettings: () => (
|
||||
<div>
|
||||
<div data-settings-section="offline"><h3>Offline</h3></div>
|
||||
<div data-settings-section="outbox"><h3>Outbox</h3></div>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
vi.mock('@/components/settings/privacy-settings', () => ({
|
||||
PrivacyLinkRow: () => null,
|
||||
PrivacySettings: () => (
|
||||
<div>
|
||||
<div data-settings-section="images"><h3>Images</h3></div>
|
||||
<div data-settings-section="receipts"><h3>Read receipts</h3></div>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
vi.mock('@/components/settings/appearance-settings',() => ({ AppearanceSettings: () => <div>appearance pane</div> }));
|
||||
vi.mock('@/components/settings/accessibility-settings', () => ({ AccessibilitySettings: () => <div>accessibility pane</div> }));
|
||||
@@ -135,10 +153,44 @@ describe('settings section links', () => {
|
||||
await waitFor(() => expect(scrolled).toContain('downloads'));
|
||||
});
|
||||
|
||||
it('shows the version footer in the sidebar only with Advanced on', () => {
|
||||
// settings-32 (2026-11-04): the version card in the sidebar's footer took
|
||||
// room from the page list on short screens; it's a page at the end now.
|
||||
it('shows the version on an About page, not in the sidebar footer', () => {
|
||||
render(<SettingsApp />);
|
||||
expect(screen.queryByTestId('about-footer')).toBeNull();
|
||||
fireEvent.click(screen.getByTestId('settings-advanced-switch'));
|
||||
expect(screen.queryByTestId('about-footer')).toBeNull();
|
||||
fireEvent.click(screen.getByTestId('settings-tab-about'));
|
||||
expect(screen.getByTestId('settings-tab-about')).toHaveAttribute('aria-current', 'page');
|
||||
expect(screen.getByTestId('about-footer')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// settings-7 and settings-24 (2026-11-04): Offline is a page of its own and
|
||||
// remote images, read receipts and sender pictures moved to Privacy. Old
|
||||
// links, from notices and bookmarks, land there.
|
||||
it('lands an old /settings/backup/offline link on the Offline page', async () => {
|
||||
render(<SettingsApp linkSegments={['backup', 'offline']} />);
|
||||
expect(screen.getByTestId('settings-tab-offline')).toHaveAttribute('aria-current', 'page');
|
||||
await waitFor(() => expect(scrolled).toContain('offline'));
|
||||
});
|
||||
|
||||
it('lands old links to remote images and read receipts on Privacy', async () => {
|
||||
const first = render(<SettingsApp linkSegments={['reading', 'images']} />);
|
||||
expect(screen.getByTestId('settings-tab-privacy')).toHaveAttribute('aria-current', 'page');
|
||||
await waitFor(() => expect(scrolled).toContain('images'));
|
||||
first.unmount();
|
||||
render(<SettingsApp linkSegments={['content_senders']} />);
|
||||
expect(screen.getByTestId('settings-tab-privacy')).toHaveAttribute('aria-current', 'page');
|
||||
act(() => { window.dispatchEvent(new CustomEvent('settings-tab-change', { detail: { tab: 'composing', section: 'receipts' } })); });
|
||||
expect(screen.getByTestId('settings-tab-privacy')).toHaveAttribute('aria-current', 'page');
|
||||
await waitFor(() => expect(scrolled).toContain('receipts'));
|
||||
});
|
||||
|
||||
it('lists Offline and Privacy among the first pages, and About last', () => {
|
||||
render(<SettingsApp />);
|
||||
const ids = Array.from(document.querySelectorAll('[data-testid^="settings-tab-"]')).map((el) => el.getAttribute('data-testid'));
|
||||
expect(ids).toContain('settings-tab-offline');
|
||||
expect(ids.indexOf('settings-tab-privacy')).toBeLessThan(ids.indexOf('settings-tab-message_list'));
|
||||
expect(ids.indexOf('settings-tab-offline')).toBeLessThan(ids.indexOf('settings-tab-message_list'));
|
||||
expect(ids[ids.length - 1]).toBe('settings-tab-about');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ConfirmDialog } from '@/components/ui/confirm-dialog';
|
||||
import { toast } from '@/stores/toast-store';
|
||||
import { DebugSettings } from './debug-settings';
|
||||
import { MailBackupSettings } from '@/components/mail-transfer/mail-backup-settings';
|
||||
import { OfflineSettings } from '@/components/offline/offline-settings';
|
||||
import { productName } from '@/lib/product-name';
|
||||
|
||||
const APP_VERSION = process.env.NEXT_PUBLIC_APP_VERSION || "0.0.0";
|
||||
const GIT_COMMIT = process.env.NEXT_PUBLIC_GIT_COMMIT || "unknown";
|
||||
@@ -55,7 +55,7 @@ function VersionUpdateTag() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The About card, in the settings sidebar's footer: logo, version with the
|
||||
* The About card, on Settings > About and at the foot of the admin's list: logo, version with the
|
||||
* update tag, and the source link. A modified build must offer its own
|
||||
* source (AGPL-3.0 §13), so a configured URL replaces the upstream link.
|
||||
* Three clicks on the logo open Spam Siege, as before.
|
||||
@@ -126,9 +126,28 @@ export function AboutFooter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The Backup & reset page: keep settings (sync, export, import), fix a stale
|
||||
* view, start over, and (Advanced, where the admin allows it) debug logging.
|
||||
* The About card moved to the settings sidebar's footer.
|
||||
* Settings > About, the last entry of the page list: the About card with the
|
||||
* version and the source link. It was in the sidebar's footer, where it took
|
||||
* room from the page list on short screens.
|
||||
*/
|
||||
export function AboutSettings() {
|
||||
const t = useTranslations('uxs_structure.about');
|
||||
const appName = productName(useConfig().appName);
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<SettingsSection id="about" title={t('title', { appName })}>
|
||||
<div className="-mx-3">
|
||||
<AboutFooter />
|
||||
</div>
|
||||
</SettingsSection>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Backup & reset page: keep settings (sync, export, import), bring mail
|
||||
* in and save it as files, fix a stale view, start over, and (Advanced, where
|
||||
* the admin allows it) debug logging. Offline has a page of its own.
|
||||
*/
|
||||
export function AboutDataSettings() {
|
||||
const t = useTranslations('settings.advanced');
|
||||
@@ -218,8 +237,6 @@ export function AboutDataSettings() {
|
||||
|
||||
<MailBackupSettings />
|
||||
|
||||
<OfflineSettings />
|
||||
|
||||
<SettingsSection id="fix" title={t('sections.fix')} description={t('sections.fix_description')}>
|
||||
<SettingItem label={t('refresh_cache.label')} description={t('refresh_cache.description')} term="cache">
|
||||
<Button variant="outline" size="sm" onClick={() => setShowRefreshConfirm(true)}>
|
||||
|
||||
@@ -6,13 +6,11 @@ import { useSettingsStore } from '@/stores/settings-store';
|
||||
import { usePolicyStore } from '@/stores/policy-store';
|
||||
import { SettingsSection, SettingItem, RadioGroup, Select, ToggleSwitch } from './settings-section';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ChevronRight, Keyboard, Volume2 } from '@/components/icons';
|
||||
import { IS_LITE } from '@/lib/lite';
|
||||
import { useNoThirdPartyTraffic } from '@/hooks/use-third-party-mode';
|
||||
import { ChevronRight, Keyboard } from '@/components/icons';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { openKeyboardShortcuts } from '@/lib/app-commands';
|
||||
import { voicesForLocale } from '@/lib/read-aloud';
|
||||
import { useSpeechVoices } from '@/hooks/use-read-aloud';
|
||||
import { NOTIFICATION_SOUNDS, playNotificationSound, type NotificationSoundChoice } from '@/lib/notification-sound';
|
||||
import {
|
||||
ACCESSIBILITY_KEYS,
|
||||
FONT_SIZES,
|
||||
@@ -32,7 +30,6 @@ import {
|
||||
} from '@/lib/accessibility-presets';
|
||||
import { AccessibilityPresetPreview } from './accessibility-preset-preview';
|
||||
import { FONT_SIZES as COMPOSE_FONT_SIZES } from '@/components/email/font-size';
|
||||
import type { Density, SendDelaySeconds } from '@/stores/settings-store';
|
||||
|
||||
type SettingKey = Parameters<ReturnType<typeof useSettingsStore.getState>['updateSetting']>[0];
|
||||
|
||||
@@ -54,21 +51,19 @@ function LinkButton({ label, onClick }: { label: string; onClick: () => void })
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings > Accessibility: presets on top, then the options by topic. Rows
|
||||
* that are also everyday preferences (conversation view, undo send, sounds…)
|
||||
* are the same setting shown on their own page too, not a copy.
|
||||
* Settings > Accessibility: presets on top, then the options by topic. Only
|
||||
* settings about access live here; everyday preferences that help too
|
||||
* (density, conversation view, undo send, sounds…) stay on their own page,
|
||||
* and a link row in the matching section leads there. One setting, one place.
|
||||
*/
|
||||
export function AccessibilitySettings() {
|
||||
const t = useTranslations('settings.accessibility');
|
||||
const tRoot = useTranslations();
|
||||
const tEmail = useTranslations('settings.email_behavior');
|
||||
const tAppearance = useTranslations('settings.appearance');
|
||||
const tNotifications = useTranslations('settings.notifications');
|
||||
const tCalendar = useTranslations('calendar.settings');
|
||||
const tLinks = useTranslations('uxs_structure.a11y_links');
|
||||
const settings = useSettingsStore();
|
||||
const { updateSetting, resetSettings } = settings;
|
||||
const { isSettingLocked, isSettingHidden, getRestriction } = usePolicyStore();
|
||||
const faviconsAllowed = !useNoThirdPartyTraffic();
|
||||
const { isSettingLocked, isSettingHidden, getRestriction, isFeatureEnabled } = usePolicyStore();
|
||||
const calendarShown = (useAuthStore((s) => s.client)?.supportsCalendars() ?? false) && isFeatureEnabled('calendarEnabled');
|
||||
const [applied, setApplied] = useState<{ id: AccessibilityPresetId; changes: PresetChange[] } | null>(null);
|
||||
const [resetDone, setResetDone] = useState(false);
|
||||
const locale = useLocale();
|
||||
@@ -271,22 +266,8 @@ export function AccessibilitySettings() {
|
||||
<SettingItem label={t('plain_language.label')} description={t('plain_language.description')}>
|
||||
<ToggleSwitch checked={settings.plainLanguage} onChange={(checked) => set('plainLanguage', checked)} />
|
||||
</SettingItem>
|
||||
{!isSettingHidden('density') && (
|
||||
<SettingItem label={tAppearance('list_density.label')} description={tAppearance('list_density.description')} locked={isSettingLocked('density')}>
|
||||
<Select
|
||||
value={settings.density}
|
||||
onChange={(value) => set('density', value as Density)}
|
||||
options={[
|
||||
{ value: 'extra-compact', label: tAppearance('list_density.extra_compact') },
|
||||
{ value: 'compact', label: tAppearance('list_density.compact') },
|
||||
{ value: 'regular', label: tAppearance('list_density.regular') },
|
||||
{ value: 'comfortable', label: tAppearance('list_density.comfortable') },
|
||||
]}
|
||||
/>
|
||||
</SettingItem>
|
||||
)}
|
||||
<SettingItem label={tAppearance('toolbar_labels.label')} description={tAppearance('toolbar_labels.description')}>
|
||||
<ToggleSwitch checked={settings.showToolbarLabels} onChange={(checked) => set('showToolbarLabels', checked)} />
|
||||
<SettingItem label={tLinks('text.label')} description={tLinks('text.description')}>
|
||||
<LinkButton label={tRoot('settings.tabs.appearance')} onClick={() => goToSettings('appearance', 'text')} />
|
||||
</SettingItem>
|
||||
</SettingsSection>
|
||||
|
||||
@@ -314,12 +295,12 @@ export function AccessibilitySettings() {
|
||||
<SettingItem label={t('cvd_palette.label')} description={t('cvd_palette.description')}>
|
||||
<ToggleSwitch checked={settings.colorBlindPalette} onChange={(checked) => set('colorBlindPalette', checked)} />
|
||||
</SettingItem>
|
||||
<SettingItem label={tEmail('always_light_mode.label')} description={tEmail('always_light_mode.description')}>
|
||||
<ToggleSwitch checked={settings.emailAlwaysLightMode} onChange={(checked) => set('emailAlwaysLightMode', checked)} />
|
||||
</SettingItem>
|
||||
<SettingItem label={t('theme_link.label')} description={t('theme_link.description')}>
|
||||
<LinkButton label={t('theme_link.button')} onClick={() => goToSettings('appearance', 'theme')} />
|
||||
</SettingItem>
|
||||
<SettingItem label={tLinks('light_mode.label')} description={tLinks('light_mode.description')}>
|
||||
<LinkButton label={tRoot('settings.tabs.reading')} onClick={() => goToSettings('reading', 'display')} />
|
||||
</SettingItem>
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection id="motion" title={t('sections.motion')}>
|
||||
@@ -369,19 +350,6 @@ export function AccessibilitySettings() {
|
||||
<SettingItem label={t('open_on_hover.label')} description={t('open_on_hover.description')}>
|
||||
<ToggleSwitch checked={settings.openOnHover} onChange={(checked) => set('openOnHover', checked)} />
|
||||
</SettingItem>
|
||||
<SettingItem label={tCalendar('hover_preview')} description={tCalendar('hover_preview_desc')}>
|
||||
<Select
|
||||
value={settings.calendarHoverPreview}
|
||||
onChange={(value) => set('calendarHoverPreview', value)}
|
||||
options={[
|
||||
{ value: 'instant', label: tCalendar('hover_preview_instant') },
|
||||
{ value: 'delay-500ms', label: tCalendar('hover_preview_delay_500ms') },
|
||||
{ value: 'delay-1s', label: tCalendar('hover_preview_delay_1s') },
|
||||
{ value: 'delay-2s', label: tCalendar('hover_preview_delay_2s') },
|
||||
{ value: 'off', label: tCalendar('hover_preview_off') },
|
||||
]}
|
||||
/>
|
||||
</SettingItem>
|
||||
<SettingItem label={t('row_action_button.label')} description={t('row_action_button.description')}>
|
||||
<ToggleSwitch checked={settings.showRowActionButton} onChange={(checked) => set('showRowActionButton', checked)} />
|
||||
</SettingItem>
|
||||
@@ -391,6 +359,11 @@ export function AccessibilitySettings() {
|
||||
<SettingItem label={t('row_actions_link.label')} description={t('row_actions_link.description')}>
|
||||
<LinkButton label={t('row_actions_link.button')} onClick={() => goToSettings('message_list', 'actions')} />
|
||||
</SettingItem>
|
||||
{calendarShown && (
|
||||
<SettingItem label={tLinks('calendar.label')} description={tLinks('calendar.description')}>
|
||||
<LinkButton label={tRoot('settings.tabs.calendar')} onClick={() => goToSettings('calendar')} />
|
||||
</SettingItem>
|
||||
)}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection id="mail" title={t('sections.mail')}>
|
||||
@@ -474,35 +447,11 @@ export function AccessibilitySettings() {
|
||||
]}
|
||||
/>
|
||||
</SettingItem>
|
||||
<SettingItem label={tEmail('group_conversations.label')} description={tEmail('group_conversations.description')}>
|
||||
<ToggleSwitch checked={!settings.disableThreading} onChange={(checked) => set('disableThreading', !checked)} />
|
||||
<SettingItem label={tLinks('list.label')} description={tLinks('list.description')}>
|
||||
<LinkButton label={tRoot('settings.tabs.message_list')} onClick={() => goToSettings('message_list', 'display')} />
|
||||
</SettingItem>
|
||||
{!isSettingHidden('showPreview') && (
|
||||
<SettingItem label={tEmail('show_preview.label')} description={tEmail('show_preview.description')} locked={isSettingLocked('showPreview')}>
|
||||
<ToggleSwitch checked={settings.showPreview} onChange={(checked) => set('showPreview', checked)} />
|
||||
</SettingItem>
|
||||
)}
|
||||
{!IS_LITE && faviconsAllowed && (
|
||||
<SettingItem label={tRoot('settings.advanced.sender_favicons.label')} description={tRoot('settings.advanced.sender_favicons.description')}>
|
||||
<ToggleSwitch checked={settings.senderFavicons} onChange={(checked) => set('senderFavicons', checked)} />
|
||||
</SettingItem>
|
||||
)}
|
||||
{!isSettingHidden('markAsReadDelay') && (
|
||||
<SettingItem label={tEmail('mark_read.label')} description={tEmail('mark_read.description')} locked={isSettingLocked('markAsReadDelay')}>
|
||||
<Select
|
||||
value={settings.markAsReadDelay.toString()}
|
||||
onChange={(value) => set('markAsReadDelay', parseInt(value))}
|
||||
options={[
|
||||
{ value: '0', label: tEmail('mark_read.instant') },
|
||||
{ value: '3000', label: tEmail('mark_read.delay_3s') },
|
||||
{ value: '5000', label: tEmail('mark_read.delay_5s') },
|
||||
{ value: '-1', label: tEmail('mark_read.never') },
|
||||
]}
|
||||
/>
|
||||
</SettingItem>
|
||||
)}
|
||||
<SettingItem label={tEmail('return_to_list_after_action.label')} description={tEmail('return_to_list_after_action.description')}>
|
||||
<ToggleSwitch checked={settings.returnToListAfterAction} onChange={(checked) => set('returnToListAfterAction', checked)} />
|
||||
<SettingItem label={tLinks('reading.label')} description={tLinks('reading.description')}>
|
||||
<LinkButton label={tRoot('settings.tabs.reading')} onClick={() => goToSettings('reading', 'opening')} />
|
||||
</SettingItem>
|
||||
</SettingsSection>
|
||||
|
||||
@@ -514,18 +463,6 @@ export function AccessibilitySettings() {
|
||||
options={toastOptions}
|
||||
/>
|
||||
</SettingItem>
|
||||
<SettingItem label={tEmail('send_delay.label')} description={tEmail('send_delay.description')}>
|
||||
<Select
|
||||
value={String(settings.sendDelaySeconds)}
|
||||
onChange={(value) => set('sendDelaySeconds', Number(value) as SendDelaySeconds)}
|
||||
options={[
|
||||
{ value: '0', label: tEmail('send_delay.off') },
|
||||
{ value: '10', label: tEmail('send_delay.seconds', { seconds: 10 }) },
|
||||
{ value: '30', label: tEmail('send_delay.seconds', { seconds: 30 }) },
|
||||
{ value: '60', label: tEmail('send_delay.seconds', { seconds: 60 }) },
|
||||
]}
|
||||
/>
|
||||
</SettingItem>
|
||||
{!isSettingHidden('sendConfirmation') && (
|
||||
<SettingItem label={t('confirm_send.label')} description={t('confirm_send.description')} locked={isSettingLocked('sendConfirmation')}>
|
||||
<ToggleSwitch checked={settings.sendConfirmation} onChange={(checked) => set('sendConfirmation', checked)} />
|
||||
@@ -537,50 +474,18 @@ export function AccessibilitySettings() {
|
||||
<SettingItem label={t('confirm_archive.label')} description={t('confirm_archive.description')}>
|
||||
<ToggleSwitch checked={settings.confirmArchive} onChange={(checked) => set('confirmArchive', checked)} />
|
||||
</SettingItem>
|
||||
<SettingItem label={tEmail('confirm_bulk_delete.label')} description={tEmail('confirm_bulk_delete.description')}>
|
||||
<ToggleSwitch checked={settings.confirmBulkDelete} onChange={(checked) => set('confirmBulkDelete', checked)} />
|
||||
</SettingItem>
|
||||
<SettingItem label={t('send_warnings_link.label')} description={t('send_warnings_link.description')}>
|
||||
<SettingItem label={t('send_warnings_link.label')} description={tLinks('send_warnings_description')}>
|
||||
<LinkButton label={t('send_warnings_link.button')} onClick={() => goToSettings('composing', 'sending')} />
|
||||
</SettingItem>
|
||||
<SettingItem label={tLinks('bulk_delete.label')} description={tLinks('bulk_delete.description')}>
|
||||
<LinkButton label={tRoot('settings.tabs.message_list')} onClick={() => goToSettings('message_list', 'delete')} />
|
||||
</SettingItem>
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection id="alerts" title={t('sections.alerts')}>
|
||||
<SettingItem label={t('visual_alerts.label')} description={t('visual_alerts.description')}>
|
||||
<ToggleSwitch checked={settings.visualAlerts} onChange={(checked) => set('visualAlerts', checked)} />
|
||||
</SettingItem>
|
||||
<SettingItem label={tNotifications('email.sound')} description={tNotifications('email.sound_desc')}>
|
||||
<ToggleSwitch checked={settings.emailNotificationSound} onChange={(checked) => set('emailNotificationSound', checked)} />
|
||||
</SettingItem>
|
||||
<SettingItem label={tNotifications('calendar.sound')} description={tNotifications('calendar.sound_desc')}>
|
||||
<ToggleSwitch checked={settings.calendarNotificationSound} onChange={(checked) => set('calendarNotificationSound', checked)} />
|
||||
</SettingItem>
|
||||
<SettingItem label={tNotifications('sound_selection.choose')} description={tNotifications('sound_selection.choose_desc')}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => playNotificationSound(settings.notificationSoundChoice)}
|
||||
aria-label={tNotifications('test_sound')}
|
||||
title={tNotifications('test_sound')}
|
||||
>
|
||||
<Volume2 className="w-4 h-4" />
|
||||
</Button>
|
||||
<Select
|
||||
value={settings.notificationSoundChoice}
|
||||
onChange={(value) => {
|
||||
const choice = value as NotificationSoundChoice;
|
||||
set('notificationSoundChoice', choice);
|
||||
playNotificationSound(choice);
|
||||
}}
|
||||
options={NOTIFICATION_SOUNDS.map((s) => ({ value: s.id, label: tNotifications(`sounds.${s.id}`) }))}
|
||||
/>
|
||||
</div>
|
||||
</SettingItem>
|
||||
<SettingItem label={tAppearance('favicon_unread_badge.label')} description={tAppearance('favicon_unread_badge.description')}>
|
||||
<ToggleSwitch checked={settings.faviconUnreadBadge} onChange={(checked) => set('faviconUnreadBadge', checked)} />
|
||||
</SettingItem>
|
||||
<SettingItem label={t('unread_counts.label')} description={t('unread_counts.description')}>
|
||||
<RadioGroup
|
||||
value={settings.unreadCountStyle}
|
||||
@@ -595,6 +500,9 @@ export function AccessibilitySettings() {
|
||||
<SettingItem label={t('do_not_disturb.label')} description={t('do_not_disturb.description')}>
|
||||
<ToggleSwitch checked={settings.doNotDisturb} onChange={(checked) => set('doNotDisturb', checked)} />
|
||||
</SettingItem>
|
||||
<SettingItem label={tLinks('sounds.label')} description={tLinks('sounds.description')}>
|
||||
<LinkButton label={tRoot('settings.tabs.notifications')} onClick={() => goToSettings('notifications', 'sound')} />
|
||||
</SettingItem>
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection id="screen-readers" title={t('sections.screen_readers')}>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { IS_LITE } from '@/lib/lite';
|
||||
import { isPasswordSession } from '@/lib/auth/password-session';
|
||||
import { usePolicyStore } from '@/stores/policy-store';
|
||||
import { AccountArchiveSettings } from './account-archive-settings';
|
||||
import { SignOutRow } from './sign-out-row';
|
||||
import { DisplayNameSection } from './account-security-settings';
|
||||
import { EmailAddressesSection } from './identity-settings';
|
||||
import { MultipleAccountsSection } from './layout-settings';
|
||||
@@ -216,6 +217,8 @@ export function AccountSettings() {
|
||||
</span>
|
||||
</SettingItem>
|
||||
)}
|
||||
|
||||
<SignOutRow />
|
||||
</SettingsSection>
|
||||
|
||||
<EmailAddressesSection />
|
||||
|
||||
@@ -9,13 +9,14 @@ import { usePolicyStore } from '@/stores/policy-store';
|
||||
import { SettingsSection, SettingItem, Select, ToggleSwitch } from './settings-section';
|
||||
import { TemplateSettings } from './template-settings';
|
||||
import { ProofreadingSettings } from './proofreading-settings';
|
||||
import { PrivacyLinkRow } from './privacy-settings';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { X, ChevronRight } from '@/components/icons';
|
||||
|
||||
/**
|
||||
* The Composing page: the editor, replying, what happens before a send,
|
||||
* read receipts, templates (their own tab before) and a pointer to where
|
||||
* signatures live, since people look for them here.
|
||||
* templates (their own tab before) and pointers to where signatures and
|
||||
* read receipts live, since people look for them here.
|
||||
*/
|
||||
export function ComposingSettings() {
|
||||
const t = useTranslations('settings.email_behavior');
|
||||
@@ -42,8 +43,6 @@ export function ComposingSettings() {
|
||||
signatureNewMailEnabled,
|
||||
signatureReplyForwardEnabled,
|
||||
signatureSeparatorEnabled,
|
||||
requestReadReceiptDefault,
|
||||
readReceiptResponse,
|
||||
updateSetting,
|
||||
} = useSettingsStore();
|
||||
const { client } = useAuthStore();
|
||||
@@ -246,27 +245,8 @@ export function ComposingSettings() {
|
||||
onChange={(checked) => updateSetting('placeholderWarningEnabled', checked)}
|
||||
/>
|
||||
</SettingItem>
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection id="receipts" title={tPage('sections.receipts')}>
|
||||
<SettingItem label={t('request_read_receipt.label')} description={t('request_read_receipt.description')}>
|
||||
<ToggleSwitch
|
||||
checked={requestReadReceiptDefault}
|
||||
onChange={(checked) => updateSetting('requestReadReceiptDefault', checked)}
|
||||
/>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem label={t('read_receipt_response.label')} description={t('read_receipt_response.description')}>
|
||||
<Select
|
||||
value={readReceiptResponse}
|
||||
onChange={(value) => updateSetting('readReceiptResponse', value as 'ask' | 'always' | 'never')}
|
||||
options={[
|
||||
{ value: 'ask', label: t('read_receipt_response.ask') },
|
||||
{ value: 'always', label: t('read_receipt_response.always') },
|
||||
{ value: 'never', label: t('read_receipt_response.never') },
|
||||
]}
|
||||
/>
|
||||
</SettingItem>
|
||||
<PrivacyLinkRow topic="receipts" />
|
||||
</SettingsSection>
|
||||
|
||||
{templatesEnabled && <TemplateSettings />}
|
||||
|
||||
@@ -9,37 +9,30 @@ import type { ArchiveMode, HoverAction, MvInboxType, SwipeAction } from '@/store
|
||||
import { ALL_HOVER_ACTIONS } from '@/stores/settings-store';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useEmailStore } from '@/stores/email-store';
|
||||
import { useFaviconRefreshStore } from '@/stores/favicon-refresh-store';
|
||||
import { toast } from '@/stores/toast-store';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { IS_LITE } from '@/lib/lite';
|
||||
import { useNoThirdPartyTraffic } from '@/hooks/use-third-party-mode';
|
||||
import { SettingsSection, SettingItem, Select, ToggleSwitch } from './settings-section';
|
||||
import { MessageListOrderSettings } from './message-list-order-settings';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { AlertTriangle, FolderSync, Loader2, RefreshCw } from '@/components/icons';
|
||||
import { PrivacyLinkRow } from './privacy-settings';
|
||||
import { AlertTriangle, FolderSync, Loader2 } from '@/components/icons';
|
||||
import { usePolicyStore } from '@/stores/policy-store';
|
||||
import { allowEmailMove, createMailboxWithHooks, emitEmailMove } from '@/lib/plugin-mail-hooks';
|
||||
import type { Mailbox } from '@/lib/jmap/types';
|
||||
import type { SearchDefaultScope } from '@/lib/search-default-scope';
|
||||
|
||||
/**
|
||||
* The Message list page: what the list shows, who the senders look like,
|
||||
* what one hover or swipe does, and what Delete and Archive mean. These
|
||||
* rows used to be spread over Reading, Layout and Appearance.
|
||||
* The Message list page: what the list shows, what one hover or swipe does,
|
||||
* and what Delete and Archive mean. These rows used to be spread over
|
||||
* Reading, Layout and Appearance. Sender pictures from the web are on
|
||||
* Privacy, with a link here.
|
||||
*/
|
||||
export function MessageListSettings() {
|
||||
const t = useTranslations('settings.email_behavior');
|
||||
// Sender icons come from an outside service: no setting for them in the mode (#903).
|
||||
const faviconsAllowed = !useNoThirdPartyTraffic();
|
||||
const tPage = useTranslations('settings.message_list');
|
||||
const tAppearance = useTranslations('settings.appearance');
|
||||
const tAdvanced = useTranslations('settings.advanced');
|
||||
const [isReorganizing, setIsReorganizing] = useState(false);
|
||||
const [reorganizeResult, setReorganizeResult] = useState<string | null>(null);
|
||||
const { isSettingLocked, isSettingHidden, isFeatureEnabled } = usePolicyStore();
|
||||
const archiveActionEnabled = isFeatureEnabled('archiveActionEnabled');
|
||||
const refreshFavicons = useFaviconRefreshStore((s) => s.refresh);
|
||||
// Swipes exist on touch screens only; a mouse user never sees these rows.
|
||||
const touchDevice = useMediaQuery('(pointer: coarse)');
|
||||
// With the Archive action off, a stored 'archive' swipe does nothing (the row
|
||||
@@ -75,8 +68,6 @@ export function MessageListSettings() {
|
||||
interfaceLayout,
|
||||
mvInboxType,
|
||||
tintListRowsByTag,
|
||||
senderFavicons,
|
||||
showAvatarsInJunk,
|
||||
updateSetting,
|
||||
} = useSettingsStore();
|
||||
|
||||
@@ -217,33 +208,8 @@ export function MessageListSettings() {
|
||||
onChange={(checked) => updateSetting('showVerificationCodes', checked)}
|
||||
/>
|
||||
</SettingItem>
|
||||
</SettingsSection>
|
||||
|
||||
{/* Sender favicons come from the /api/favicon proxy, absent in the static Lite build (components/ui/avatar.tsx). */}
|
||||
<SettingsSection id="senders" title={tPage('sections.senders')}>
|
||||
{!IS_LITE && faviconsAllowed && (
|
||||
<SettingItem label={tAdvanced('sender_favicons.label')} description={tAdvanced('sender_favicons.description')}>
|
||||
<ToggleSwitch checked={senderFavicons} onChange={(checked) => updateSetting('senderFavicons', checked)} />
|
||||
</SettingItem>
|
||||
)}
|
||||
|
||||
{!IS_LITE && faviconsAllowed && senderFavicons && (
|
||||
<SettingItem nested label={tAdvanced('sender_favicons.refresh_label')} description={tAdvanced('sender_favicons.refresh_description')}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => { refreshFavicons(); toast.success(tAdvanced('sender_favicons.refresh_done')); }}
|
||||
className="text-xs h-7"
|
||||
>
|
||||
<RefreshCw className="w-3.5 h-3.5 me-1" />
|
||||
{tAdvanced('sender_favicons.refresh_button')}
|
||||
</Button>
|
||||
</SettingItem>
|
||||
)}
|
||||
|
||||
<SettingItem label={tAdvanced('show_avatars_in_junk.label')} description={tAdvanced('show_avatars_in_junk.description')} advanced>
|
||||
<ToggleSwitch checked={showAvatarsInJunk} onChange={(checked) => updateSetting('showAvatarsInJunk', checked)} />
|
||||
</SettingItem>
|
||||
<PrivacyLinkRow topic="senders" />
|
||||
</SettingsSection>
|
||||
|
||||
{(isFeatureEnabled('hoverActionsConfigEnabled') || touchDevice) && (
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSettingsStore } from '@/stores/settings-store';
|
||||
import { usePolicyStore } from '@/stores/policy-store';
|
||||
import { useContactStore } from '@/stores/contact-store';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useFaviconRefreshStore } from '@/stores/favicon-refresh-store';
|
||||
import { toast } from '@/stores/toast-store';
|
||||
import { useNoThirdPartyTraffic, useRemoteContentBlockedByOrg } from '@/hooks/use-third-party-mode';
|
||||
import { IS_LITE } from '@/lib/lite';
|
||||
import { TrustedSendersModal } from '@/components/trusted-senders-modal';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ChevronRight, RefreshCw } from '@/components/icons';
|
||||
import { SettingsSection, SettingItem, Select, ToggleSwitch } from './settings-section';
|
||||
import { DesktopTelemetrySettings } from './desktop-telemetry-settings';
|
||||
|
||||
/** Opens another settings page (and section) the way deep links do. */
|
||||
export function goToSettings(tab: string, section?: string) {
|
||||
window.dispatchEvent(new CustomEvent('settings-tab-change', { detail: section ? { tab, section } : tab }));
|
||||
}
|
||||
|
||||
/**
|
||||
* A row that only points to the page holding a setting now, so people who
|
||||
* look for it in the old place still find it (one setting, one place).
|
||||
*/
|
||||
export function SettingsLinkRow({ label, description, button, onClick }: { label: string; description: string; button: string; onClick: () => void }) {
|
||||
return (
|
||||
<SettingItem label={label} description={description}>
|
||||
<Button variant="outline" size="sm" onClick={onClick}>
|
||||
{button}
|
||||
<ChevronRight className="w-4 h-4 ms-1 rtl:rotate-180" />
|
||||
</Button>
|
||||
</SettingItem>
|
||||
);
|
||||
}
|
||||
|
||||
/** The link to Privacy on the pages these settings came from. */
|
||||
export function PrivacyLinkRow({ topic }: { topic: 'images' | 'receipts' | 'senders' }) {
|
||||
const t = useTranslations('uxs_structure.privacy_links');
|
||||
return (
|
||||
<SettingsLinkRow
|
||||
label={t(`${topic}_label`)}
|
||||
description={t('description')}
|
||||
button={t('button')}
|
||||
onClick={() => goToSettings('privacy', topic)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/** Remote images and trusted senders (the former Content & Senders tab, then a section of Reading). */
|
||||
function RemoteContentSection() {
|
||||
// No third-party traffic (#903), or the organisation locked "block":
|
||||
// remote content never loads, so the setting shows as the organisation's,
|
||||
// set to block, and trusting senders has no effect.
|
||||
const remoteBlockedByOrg = useRemoteContentBlockedByOrg();
|
||||
const tLock = useTranslations('remote_content_lock');
|
||||
const t = useTranslations('settings.email_behavior');
|
||||
const tPage = useTranslations('settings.reading_page');
|
||||
const [showTrustedModal, setShowTrustedModal] = useState(false);
|
||||
const { isSettingLocked, isSettingHidden, isFeatureEnabled } = usePolicyStore();
|
||||
const { externalContentPolicy, trustedSenders, trustedSendersAddressBook, updateSetting } = useSettingsStore();
|
||||
const { trustedSenderEmails, trustedSendersLoaded, loadTrustedSendersBook } = useContactStore();
|
||||
const client = useAuthStore((state) => state.client);
|
||||
|
||||
// Load the address book so the count reflects the synced senders, not 0.
|
||||
useEffect(() => {
|
||||
if (trustedSendersAddressBook && client && !trustedSendersLoaded) {
|
||||
loadTrustedSendersBook(client);
|
||||
}
|
||||
}, [trustedSendersAddressBook, client, trustedSendersLoaded, loadTrustedSendersBook]);
|
||||
|
||||
const trustedCount = () => {
|
||||
const count = trustedSendersAddressBook ? trustedSenderEmails.length : trustedSenders.length;
|
||||
if (count === 0) return t('trusted_senders.count_zero');
|
||||
if (count === 1) return t('trusted_senders.count_one');
|
||||
return t('trusted_senders.count_other', { count });
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingsSection id="images" title={tPage('sections.images')} description={tPage('sections.images_description')}>
|
||||
{isFeatureEnabled('externalContentEnabled') && !isSettingHidden('externalContentPolicy') && (
|
||||
<SettingItem label={t('external_content.label')} description={t('external_content.description')} locked={isSettingLocked('externalContentPolicy') || remoteBlockedByOrg}>
|
||||
<Select
|
||||
value={remoteBlockedByOrg ? 'block' : externalContentPolicy}
|
||||
onChange={(value) => updateSetting('externalContentPolicy', value as 'ask' | 'block' | 'allow')}
|
||||
options={[
|
||||
{ value: 'ask', label: t('external_content.ask') },
|
||||
{ value: 'block', label: t('external_content.block') },
|
||||
{ value: 'allow', label: t('external_content.allow') },
|
||||
]}
|
||||
/>
|
||||
</SettingItem>
|
||||
)}
|
||||
|
||||
<SettingItem
|
||||
label={t('trusted_senders.label')}
|
||||
description={remoteBlockedByOrg ? tLock('trusted_senders_note') : t('trusted_senders.description')}
|
||||
>
|
||||
<button
|
||||
onClick={() => setShowTrustedModal(true)}
|
||||
className="flex items-center gap-2 px-3 py-1.5 bg-muted hover:bg-accent rounded-md transition-colors"
|
||||
>
|
||||
<span className="text-sm text-foreground">{trustedCount()}</span>
|
||||
<ChevronRight className="w-4 h-4 text-muted-foreground rtl:rotate-180" />
|
||||
</button>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem nested label={t('trusted_senders.use_address_book_label')} description={t('trusted_senders.use_address_book_description')} locked={remoteBlockedByOrg}>
|
||||
<ToggleSwitch
|
||||
checked={!!trustedSendersAddressBook}
|
||||
onChange={(checked) => updateSetting('trustedSendersAddressBook', checked)}
|
||||
/>
|
||||
</SettingItem>
|
||||
|
||||
<TrustedSendersModal
|
||||
isOpen={showTrustedModal}
|
||||
onClose={() => setShowTrustedModal(false)}
|
||||
readOnly={remoteBlockedByOrg}
|
||||
/>
|
||||
</SettingsSection>
|
||||
);
|
||||
}
|
||||
|
||||
/** Asking for read receipts and answering them (a section of Composing before). */
|
||||
function ReadReceiptsSection() {
|
||||
const t = useTranslations('settings.email_behavior');
|
||||
const tPage = useTranslations('settings.composing_page');
|
||||
const { requestReadReceiptDefault, readReceiptResponse, updateSetting } = useSettingsStore();
|
||||
return (
|
||||
<SettingsSection id="receipts" title={tPage('sections.receipts')}>
|
||||
<SettingItem label={t('request_read_receipt.label')} description={t('request_read_receipt.description')}>
|
||||
<ToggleSwitch
|
||||
checked={requestReadReceiptDefault}
|
||||
onChange={(checked) => updateSetting('requestReadReceiptDefault', checked)}
|
||||
/>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem label={t('read_receipt_response.label')} description={t('read_receipt_response.description')}>
|
||||
<Select
|
||||
value={readReceiptResponse}
|
||||
onChange={(value) => updateSetting('readReceiptResponse', value as 'ask' | 'always' | 'never')}
|
||||
options={[
|
||||
{ value: 'ask', label: t('read_receipt_response.ask') },
|
||||
{ value: 'always', label: t('read_receipt_response.always') },
|
||||
{ value: 'never', label: t('read_receipt_response.never') },
|
||||
]}
|
||||
/>
|
||||
</SettingItem>
|
||||
</SettingsSection>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sender pictures fetched from senders' websites (a section of Message list
|
||||
* before). They come from the /api/favicon proxy, absent in the static Lite
|
||||
* build, and from an outside service, so not in the no-third-party mode (#903).
|
||||
*/
|
||||
function SenderPicturesSection() {
|
||||
const tPage = useTranslations('settings.message_list');
|
||||
const tAdvanced = useTranslations('settings.advanced');
|
||||
const faviconsAllowed = !useNoThirdPartyTraffic();
|
||||
const refreshFavicons = useFaviconRefreshStore((s) => s.refresh);
|
||||
const { senderFavicons, showAvatarsInJunk, updateSetting } = useSettingsStore();
|
||||
return (
|
||||
<SettingsSection id="senders" title={tPage('sections.senders')}>
|
||||
{!IS_LITE && faviconsAllowed && (
|
||||
<SettingItem label={tAdvanced('sender_favicons.label')} description={tAdvanced('sender_favicons.description')}>
|
||||
<ToggleSwitch checked={senderFavicons} onChange={(checked) => updateSetting('senderFavicons', checked)} />
|
||||
</SettingItem>
|
||||
)}
|
||||
|
||||
{!IS_LITE && faviconsAllowed && senderFavicons && (
|
||||
<SettingItem nested label={tAdvanced('sender_favicons.refresh_label')} description={tAdvanced('sender_favicons.refresh_description')}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => { refreshFavicons(); toast.success(tAdvanced('sender_favicons.refresh_done')); }}
|
||||
className="text-xs h-7"
|
||||
>
|
||||
<RefreshCw className="w-3.5 h-3.5 me-1" />
|
||||
{tAdvanced('sender_favicons.refresh_button')}
|
||||
</Button>
|
||||
</SettingItem>
|
||||
)}
|
||||
|
||||
<SettingItem label={tAdvanced('show_avatars_in_junk.label')} description={tAdvanced('show_avatars_in_junk.description')} advanced>
|
||||
<ToggleSwitch checked={showAvatarsInJunk} onChange={(checked) => updateSetting('showAvatarsInJunk', checked)} />
|
||||
</SettingItem>
|
||||
</SettingsSection>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings > Privacy: what tells others about you or fetches from outside.
|
||||
* Remote content and trusted senders, read receipts, sender pictures from
|
||||
* the web, and in the desktop app usage info and crash reports. Background
|
||||
* notifications through the push relay stay on Notifications, with a link.
|
||||
*/
|
||||
export function PrivacySettings() {
|
||||
const t = useTranslations('uxs_structure.privacy');
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<RemoteContentSection />
|
||||
<ReadReceiptsSection />
|
||||
<SenderPicturesSection />
|
||||
<DesktopTelemetrySettings />
|
||||
<SettingsSection id="push" title={t('push_title')}>
|
||||
<SettingsLinkRow
|
||||
label={t('push_link.label')}
|
||||
description={t('push_link.description')}
|
||||
button={t('push_link.button')}
|
||||
onClick={() => goToSettings('notifications', 'push')}
|
||||
/>
|
||||
</SettingsSection>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,33 +1,23 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSettingsStore, type AttachmentPosition } from '@/stores/settings-store';
|
||||
import { SettingsSection, SettingItem, Select, ToggleSwitch } from './settings-section';
|
||||
import { DownloadsSettings } from './downloads-settings';
|
||||
import { TrustedSendersModal } from '@/components/trusted-senders-modal';
|
||||
import { PrivacyLinkRow } from './privacy-settings';
|
||||
import { ChevronRight } from '@/components/icons';
|
||||
import { usePolicyStore } from '@/stores/policy-store';
|
||||
import { useContactStore } from '@/stores/contact-store';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useRemoteContentBlockedByOrg } from '@/hooks/use-third-party-mode';
|
||||
|
||||
/**
|
||||
* The Reading page: the open message. Opening it, how its body is drawn,
|
||||
* remote images and trusted senders (the former Content & Senders tab), and
|
||||
* attachments, and (Advanced) how downloads are named, the former Downloads
|
||||
* tab. List, delete and swipe rows live on the Message list page.
|
||||
* tab. Remote images and trusted senders are on Privacy, with a link here;
|
||||
* list, delete and swipe rows live on the Message list page.
|
||||
*/
|
||||
export function ReadingSettings() {
|
||||
// No third-party traffic (#903), or the organisation locked "block":
|
||||
// remote content never loads, so the setting shows as the organisation's,
|
||||
// set to block, and trusting senders has no effect.
|
||||
const remoteBlockedByOrg = useRemoteContentBlockedByOrg();
|
||||
const tLock = useTranslations('remote_content_lock');
|
||||
const t = useTranslations('settings.email_behavior');
|
||||
const tPage = useTranslations('settings.reading_page');
|
||||
const [showTrustedModal, setShowTrustedModal] = useState(false);
|
||||
const { isSettingLocked, isSettingHidden, isFeatureEnabled } = usePolicyStore();
|
||||
const { isSettingLocked, isSettingHidden } = usePolicyStore();
|
||||
|
||||
const {
|
||||
markAsReadDelay,
|
||||
@@ -37,28 +27,9 @@ export function ReadingSettings() {
|
||||
hideInlineImageAttachments,
|
||||
attachmentImagePreviewsEnabled,
|
||||
messageSpacing,
|
||||
externalContentPolicy,
|
||||
emailAlwaysLightMode,
|
||||
trustedSenders,
|
||||
trustedSendersAddressBook,
|
||||
updateSetting,
|
||||
} = useSettingsStore();
|
||||
const { trustedSenderEmails, trustedSendersLoaded, loadTrustedSendersBook } = useContactStore();
|
||||
const client = useAuthStore((state) => state.client);
|
||||
|
||||
// Load the address book so the count reflects the synced senders, not 0.
|
||||
useEffect(() => {
|
||||
if (trustedSendersAddressBook && client && !trustedSendersLoaded) {
|
||||
loadTrustedSendersBook(client);
|
||||
}
|
||||
}, [trustedSendersAddressBook, client, trustedSendersLoaded, loadTrustedSendersBook]);
|
||||
|
||||
const getTrustedSendersCount = () => {
|
||||
const count = trustedSendersAddressBook ? trustedSenderEmails.length : trustedSenders.length;
|
||||
if (count === 0) return t('trusted_senders.count_zero');
|
||||
if (count === 1) return t('trusted_senders.count_one');
|
||||
return t('trusted_senders.count_other', { count });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
@@ -119,50 +90,8 @@ export function ReadingSettings() {
|
||||
onChange={(checked) => updateSetting('emailAlwaysLightMode', checked)}
|
||||
/>
|
||||
</SettingItem>
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection id="images" title={tPage('sections.images')} description={tPage('sections.images_description')}>
|
||||
{isFeatureEnabled('externalContentEnabled') && !isSettingHidden('externalContentPolicy') && (
|
||||
<SettingItem label={t('external_content.label')} description={t('external_content.description')} locked={isSettingLocked('externalContentPolicy') || remoteBlockedByOrg}>
|
||||
<Select
|
||||
value={remoteBlockedByOrg ? 'block' : externalContentPolicy}
|
||||
onChange={(value) =>
|
||||
updateSetting('externalContentPolicy', value as 'ask' | 'block' | 'allow')
|
||||
}
|
||||
options={[
|
||||
{ value: 'ask', label: t('external_content.ask') },
|
||||
{ value: 'block', label: t('external_content.block') },
|
||||
{ value: 'allow', label: t('external_content.allow') },
|
||||
]}
|
||||
/>
|
||||
</SettingItem>
|
||||
)}
|
||||
|
||||
<SettingItem
|
||||
label={t('trusted_senders.label')}
|
||||
description={remoteBlockedByOrg ? tLock('trusted_senders_note') : t('trusted_senders.description')}
|
||||
>
|
||||
<button
|
||||
onClick={() => setShowTrustedModal(true)}
|
||||
className="flex items-center gap-2 px-3 py-1.5 bg-muted hover:bg-accent rounded-md transition-colors"
|
||||
>
|
||||
<span className="text-sm text-foreground">{getTrustedSendersCount()}</span>
|
||||
<ChevronRight className="w-4 h-4 text-muted-foreground" />
|
||||
</button>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem nested label={t('trusted_senders.use_address_book_label')} description={t('trusted_senders.use_address_book_description')} locked={remoteBlockedByOrg}>
|
||||
<ToggleSwitch
|
||||
checked={!!trustedSendersAddressBook}
|
||||
onChange={(checked) => updateSetting('trustedSendersAddressBook', checked)}
|
||||
/>
|
||||
</SettingItem>
|
||||
|
||||
<TrustedSendersModal
|
||||
isOpen={showTrustedModal}
|
||||
onClose={() => setShowTrustedModal(false)}
|
||||
readOnly={remoteBlockedByOrg}
|
||||
/>
|
||||
<PrivacyLinkRow topic="images" />
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection id="attachments" title={tPage('sections.attachments')}>
|
||||
|
||||
@@ -34,7 +34,10 @@ import { CalendarSettings } from '@/components/settings/calendar-settings';
|
||||
import { CalendarManagementSettings } from '@/components/settings/calendar-management-settings';
|
||||
import { AddressBookManagementSettings } from '@/components/settings/address-book-management-settings';
|
||||
import { FilterSettings } from '@/components/settings/filter-settings';
|
||||
import { AboutDataSettings, AboutFooter } from '@/components/settings/about-data-settings';
|
||||
import { AboutDataSettings, AboutSettings } from '@/components/settings/about-data-settings';
|
||||
import { PrivacySettings } from '@/components/settings/privacy-settings';
|
||||
import { OfflineSettings } from '@/components/offline/offline-settings';
|
||||
import { IS_LITE } from '@/lib/lite';
|
||||
import { HelpLinks } from '@/components/layout/help-links';
|
||||
import { OrganizeSettings } from '@/components/settings/organize-settings';
|
||||
import { AccountSecuritySettings } from '@/components/settings/account-security-settings';
|
||||
@@ -82,6 +85,8 @@ import {
|
||||
getByPath,
|
||||
matchesAllWords,
|
||||
settingsSubResults,
|
||||
settingsTabLabelKey,
|
||||
subResultText,
|
||||
tabKeywords,
|
||||
tabSearchEntries,
|
||||
ARCHIVE_ACTION_SEARCH_PATHS,
|
||||
@@ -96,8 +101,8 @@ import { scrollBehavior } from '@/lib/reduced-motion';
|
||||
|
||||
type Tab = SettingsSearchTab;
|
||||
|
||||
// `top` and `bottom` have no heading: the first five pages, and Backup & reset
|
||||
// after a rule at the end.
|
||||
// `top` and `bottom` have no heading: the first pages, and Backup & reset and
|
||||
// About after a rule at the end.
|
||||
type TabGroup = SettingsTabGroup;
|
||||
|
||||
// A plugin that exposes a `settings-section` slot gets its own first-class
|
||||
@@ -153,13 +158,25 @@ interface SettingsTarget {
|
||||
section: string | null;
|
||||
}
|
||||
|
||||
// Sections that moved to a page of their own or to Privacy (2026-11), so a
|
||||
// link to `/settings/backup/offline` or `/settings/reading/images` still
|
||||
// lands on the section.
|
||||
const MOVED_SECTIONS: Record<string, Record<string, string>> = {
|
||||
backup: { offline: 'offline' },
|
||||
reading: { images: 'privacy' },
|
||||
composing: { receipts: 'privacy' },
|
||||
message_list: { senders: 'privacy' },
|
||||
};
|
||||
|
||||
/**
|
||||
* Migrates a legacy tab id to the page and section that hold it now. An
|
||||
* explicit section wins over the legacy one.
|
||||
*/
|
||||
function migrateTarget(raw: string, section: string | null = null): { tab: string; section: string | null } {
|
||||
const tab = raw in LEGACY_TAB_MAP ? LEGACY_TAB_MAP[raw] : raw;
|
||||
return { tab, section: section ?? (raw in LEGACY_SECTION_MAP ? LEGACY_SECTION_MAP[raw] : null) };
|
||||
const target = section ?? (raw in LEGACY_SECTION_MAP ? LEGACY_SECTION_MAP[raw] : null);
|
||||
const moved = target ? MOVED_SECTIONS[tab]?.[target] : undefined;
|
||||
return { tab: moved ?? tab, section: target };
|
||||
}
|
||||
|
||||
function hasOneShotDeepLink(): boolean {
|
||||
@@ -231,6 +248,7 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
const router = useRouter();
|
||||
const t = useTranslations('settings');
|
||||
const tSidebar = useTranslations('sidebar');
|
||||
const tRoot = useTranslations();
|
||||
const tShell = useTranslations('shell');
|
||||
const tDelegates = useTranslations('delegates');
|
||||
const tPalette = useTranslations('command_palette');
|
||||
@@ -337,7 +355,6 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
// plugins/themes/sidebar apps).
|
||||
const touchScreen = useMediaQuery('(pointer: coarse)');
|
||||
const mountainViewLayout = useSettingsStore((s) => s.interfaceLayout === 'mountain-view');
|
||||
const showAdvanced = useSettingsStore((s) => s.showAdvancedSettings);
|
||||
const multiAccount = useAccountStore((s) => s.accounts.length > 0);
|
||||
const { tabSearchHaystacks, tabSubResults } = useMemo(() => {
|
||||
const haystacks: Partial<Record<Tab, string>> = {};
|
||||
@@ -351,7 +368,7 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
return true;
|
||||
};
|
||||
for (const tabId of Object.keys(tabSearchEntries) as Tab[]) {
|
||||
const tabName = getByPath(messages, `settings.tabs.${tabId}`);
|
||||
const tabName = getByPath(messages, settingsTabLabelKey(tabId));
|
||||
const list = settingsSubResults(messages, tabId, appName, shown)
|
||||
// A section titled like its page is the page itself.
|
||||
.filter((r) => r.label !== tabName);
|
||||
@@ -364,7 +381,7 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
haystacks[tabId] = [
|
||||
tabId.replace(/_/g, ' '),
|
||||
tabKeywords[tabId] ?? '',
|
||||
...list.flatMap((r) => [r.label, r.description ?? '']),
|
||||
...list.map(subResultText),
|
||||
].join(' ').toLowerCase();
|
||||
}
|
||||
if (installedPlugins.length) {
|
||||
@@ -378,7 +395,7 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
}).join(' ');
|
||||
haystacks.addons = `${haystacks.addons ?? ''} ${haystackText}`.toLowerCase();
|
||||
const pluginSubs: SubResult[] = installedPlugins.flatMap((p) => {
|
||||
const items: SubResult[] = [{ label: p.name, description: p.description }];
|
||||
const items: SubResult[] = [{ label: p.name, description: p.description, keywords: p.author }];
|
||||
if (p.settingsSchema) {
|
||||
for (const schema of Object.values(p.settingsSchema)) {
|
||||
items.push({
|
||||
@@ -398,7 +415,7 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
haystacks.appearance = `${haystacks.appearance ?? ''} ${text}`.toLowerCase();
|
||||
subs.appearance = [
|
||||
...(subs.appearance ?? []),
|
||||
...installedThemes.map((th) => ({ label: th.name, description: th.description })),
|
||||
...installedThemes.map((th) => ({ label: th.name, description: th.description, keywords: th.author })),
|
||||
];
|
||||
}
|
||||
if (sidebarAppsList.length) {
|
||||
@@ -668,9 +685,12 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
// You and the app
|
||||
{ id: 'account', label: t('tabs.account'), icon: tabIcons.account, group: 'top' },
|
||||
...(stalwartFeaturesEnabled ? [{ id: 'security' as Tab, label: t('tabs.security'), icon: tabIcons.security, group: 'top' as TabGroup }] : []),
|
||||
{ id: 'privacy', label: tRoot(settingsTabLabelKey('privacy')), icon: tabIcons.privacy, group: 'top' },
|
||||
...(supportsSharing ? [{ id: 'sharing' as Tab, label: tDelegates('tab'), icon: tabIcons.sharing, group: 'top' as TabGroup }] : []),
|
||||
{ id: 'notifications', label: t('tabs.notifications'), icon: tabIcons.notifications, group: 'top' },
|
||||
{ id: 'general', label: t('tabs.general'), icon: tabIcons.general, group: 'top' },
|
||||
// Keeping mail on this device; the static Lite build has no offline mode.
|
||||
...(!IS_LITE ? [{ id: 'offline' as Tab, label: tRoot(settingsTabLabelKey('offline')), icon: tabIcons.offline, group: 'top' as TabGroup }] : []),
|
||||
{ id: 'appearance', label: t('tabs.appearance'), icon: tabIcons.appearance, group: 'top' },
|
||||
{ id: 'accessibility', label: t('tabs.accessibility'), icon: tabIcons.accessibility, group: 'top' },
|
||||
|
||||
@@ -709,6 +729,7 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
|
||||
// After a rule at the end, with no heading.
|
||||
{ id: 'backup', label: t('tabs.backup'), icon: tabIcons.backup, group: 'bottom' },
|
||||
{ id: 'about', label: tRoot(settingsTabLabelKey('about')), icon: tabIcons.about, group: 'bottom' },
|
||||
];
|
||||
|
||||
// In scoped (shared-account) mode, restrict to the account-relevant tabs the
|
||||
@@ -736,20 +757,24 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
}))
|
||||
.filter((g) => g.items.length > 0);
|
||||
|
||||
// Every word of the query counts, in any order, like the admin search.
|
||||
// Every word of the query starts a word, in any order. A page matches by
|
||||
// its own name and keywords, or when one of its rows has all the words:
|
||||
// words spread over unrelated rows don't make a match.
|
||||
const trimmedQuery = searchQuery.trim().toLowerCase();
|
||||
const matchesQuery = (tab: TabDef) => {
|
||||
if (!trimmedQuery) return true;
|
||||
return matchesAllWords(`${tab.label} ${tabSearchHaystacks[tab.id as Tab] ?? ''}`, trimmedQuery);
|
||||
const pageText = `${tab.label} ${String(tab.id).replace(/_/g, ' ')} ${tabKeywords[tab.id as Tab] ?? ''}`;
|
||||
return matchesAllWords(pageText, trimmedQuery)
|
||||
|| (tabSubResults[tab.id as Tab] ?? []).some((r) => matchesAllWords(subResultText(r), trimmedQuery));
|
||||
};
|
||||
|
||||
const subResultsForTab = (tabId: SettingsTabId): SubResult[] => {
|
||||
if (!trimmedQuery) return [];
|
||||
const list = tabSubResults[tabId as Tab] ?? [];
|
||||
// Rows whose name matches come before rows matched by their description.
|
||||
// Rows whose name matches come before rows matched by their description or synonyms.
|
||||
const byName = (r: SubResult) => (matchesAllWords(r.label, trimmedQuery) ? 0 : 1);
|
||||
return list
|
||||
.filter((r) => matchesAllWords(`${r.label} ${r.description ?? ''}`, trimmedQuery))
|
||||
.filter((r) => matchesAllWords(subResultText(r), trimmedQuery))
|
||||
.sort((a, b) => byName(a) - byName(b))
|
||||
.slice(0, 6);
|
||||
};
|
||||
@@ -890,7 +915,10 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
)}
|
||||
{effectiveActiveTab === 'files' && <FilesSettingsComponent />}
|
||||
{effectiveActiveTab === 'addons' && <AddonsSettings />}
|
||||
{effectiveActiveTab === 'privacy' && <PrivacySettings />}
|
||||
{effectiveActiveTab === 'offline' && <OfflineSettings />}
|
||||
{effectiveActiveTab === 'backup' && <AboutDataSettings />}
|
||||
{effectiveActiveTab === 'about' && <AboutSettings />}
|
||||
{effectiveActiveTab.startsWith('plugin:') && (
|
||||
<PluginIframeSlot
|
||||
key={effectiveActiveTab}
|
||||
@@ -1043,7 +1071,6 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
|
||||
<div className="border-t border-border px-2 py-2">
|
||||
<HelpLinks className="px-3 py-2" />
|
||||
{showAdvanced && <AboutFooter />}
|
||||
<AdvancedSettingsSwitch />
|
||||
</div>
|
||||
<div className="border-t border-border px-5 py-3">
|
||||
@@ -1208,9 +1235,11 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
{/* Only the Advanced switch (and the operator's help links, when set):
|
||||
the About card is a page at the end of the list, so the footer
|
||||
doesn't take room from the pages on short screens. */}
|
||||
<div className="border-t border-border px-1 py-1">
|
||||
<HelpLinks className="px-3 py-2" />
|
||||
{showAdvanced && <AboutFooter />}
|
||||
<AdvancedSettingsSwitch />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,6 +18,9 @@ import {
|
||||
Database,
|
||||
Users,
|
||||
RotateCcw,
|
||||
CloudOff,
|
||||
EyeOff,
|
||||
Info,
|
||||
type AppIcon,
|
||||
} from '@/components/icons';
|
||||
import type { SettingsSearchTab } from '@/lib/settings-search';
|
||||
@@ -46,7 +49,10 @@ export const SETTINGS_TAB_ICONS: Record<SettingsSearchTab, AppIcon> = {
|
||||
contacts: BookUser,
|
||||
files: HardDrive,
|
||||
addons: Puzzle,
|
||||
offline: CloudOff,
|
||||
privacy: EyeOff,
|
||||
backup: Database,
|
||||
about: Info,
|
||||
};
|
||||
|
||||
export type SettingsTabGroup = 'top' | 'mail' | 'apps' | 'bottom';
|
||||
@@ -58,8 +64,8 @@ export type SettingsTabGroup = 'top' | 'mail' | 'apps' | 'bottom';
|
||||
* from the last session's hints.
|
||||
*/
|
||||
export const SETTINGS_NAV_GROUPS: readonly { group: SettingsTabGroup; tabs: readonly SettingsSearchTab[] }[] = [
|
||||
{ group: 'top', tabs: ['account', 'security', 'sharing', 'notifications', 'general', 'appearance', 'accessibility'] },
|
||||
{ group: 'top', tabs: ['account', 'security', 'privacy', 'sharing', 'notifications', 'general', 'offline', 'appearance', 'accessibility'] },
|
||||
{ group: 'mail', tabs: ['message_list', 'reading', 'composing', 'organize', 'filters', 'vacation', 'recovery'] },
|
||||
{ group: 'apps', tabs: ['calendar', 'contacts', 'files', 'addons'] },
|
||||
{ group: 'bottom', tabs: ['backup'] },
|
||||
{ group: 'bottom', tabs: ['backup', 'about'] },
|
||||
];
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { LogOut } from '@/components/icons';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { SettingItem } from './settings-section';
|
||||
|
||||
/**
|
||||
* "Sign out" on the Account page, so searching Settings for "logout" or
|
||||
* "sign out" leads somewhere. It does what the account menu's Sign out does.
|
||||
*/
|
||||
export function SignOutRow() {
|
||||
const tSidebar = useTranslations('sidebar');
|
||||
const t = useTranslations('uxs_structure.sign_out');
|
||||
const logout = useAuthStore((s) => s.logout);
|
||||
return (
|
||||
<SettingItem label={tSidebar('sign_out')} description={t('description')}>
|
||||
<Button variant="outline" size="sm" onClick={() => logout()} data-testid="settings-sign-out">
|
||||
<LogOut className="w-4 h-4 me-1" />
|
||||
{tSidebar('sign_out')}
|
||||
</Button>
|
||||
</SettingItem>
|
||||
);
|
||||
}
|
||||
@@ -98,11 +98,11 @@ async function setPolicy(browser: Browser, patch: { organisation?: Record<string
|
||||
|
||||
/** Settings > Backup & reset > Offline, switched on, and the first sync done. */
|
||||
async function enableOffline(page: Page, phone = false): Promise<void> {
|
||||
await page.goto('/settings/backup/offline');
|
||||
await page.goto('/settings/offline');
|
||||
const toggle = page.getByTestId('offline-switch');
|
||||
if (phone && !(await toggle.waitFor({ state: 'visible', timeout: 8_000 }).then(() => true, () => false))) {
|
||||
// On a phone the settings list may come first.
|
||||
await page.getByText('Backup & reset', { exact: true }).first().click();
|
||||
await page.getByTestId('settings-tab-offline').click();
|
||||
}
|
||||
await expect(toggle).toBeVisible({ timeout: 30_000 });
|
||||
if ((await toggle.getAttribute('aria-checked')) !== 'true') await toggle.click();
|
||||
|
||||
@@ -101,7 +101,7 @@ async function startOffline(): Promise<void> {
|
||||
await expect(page.getByTestId('offline-banner')).toContainText("You're offline", { timeout: 60_000 });
|
||||
// The shell can open Settings on its first page: from there through the menu, as a person would.
|
||||
if (!(await page.getByTestId('offline-switch').waitFor({ state: 'visible', timeout: 5_000 }).then(() => true, () => false))) {
|
||||
await page.getByText('Backup & reset', { exact: true }).first().click();
|
||||
await page.getByTestId('settings-tab-offline').click();
|
||||
}
|
||||
await expect(page.getByTestId('offline-switch')).toBeVisible({ timeout: 30_000 });
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
matchesAllWords,
|
||||
tabKeywords,
|
||||
settingsSubResults,
|
||||
subResultText,
|
||||
tabSearchEntries,
|
||||
withAppName,
|
||||
} from '../settings-search';
|
||||
@@ -37,9 +38,11 @@ describe('settings search index (English messages)', () => {
|
||||
expect(labelsFor('calendar')).toContain('Free scrolling');
|
||||
expect(labelsFor('calendar')).toContain('Show week numbers');
|
||||
expect(labelsFor('notifications')).toContain('Unread count on the tab and app icon');
|
||||
expect(labelsFor('message_list')).toContain('Sender favicons');
|
||||
// settings-24 (2026-11-04): sender pictures, remote images and trusted
|
||||
// senders moved from Message list and Reading to the new Privacy page.
|
||||
expect(labelsFor('privacy')).toContain('Sender favicons');
|
||||
expect(labelsFor('message_list')).toContain('Clear search when switching folders');
|
||||
expect(labelsFor('reading')).toContain('Trusted senders');
|
||||
expect(labelsFor('privacy')).toContain('Trusted senders');
|
||||
expect(labelsFor('composing')).toContain('Undo send / send delay');
|
||||
expect(labelsFor('general')).toContain('Time zone');
|
||||
expect(labelsFor('organize')).toContain('Nested tags');
|
||||
@@ -86,7 +89,92 @@ describe('settings search index (English messages)', () => {
|
||||
it('marks the rows the Advanced switch hides', () => {
|
||||
const advanced = settingsSubResults(en, 'message_list', 'Bulwark').filter((r) => r.advanced).map((r) => r.label);
|
||||
expect(advanced).toContain('Emails per page');
|
||||
expect(advanced).not.toContain('Sender favicons');
|
||||
expect(settingsSubResults(en, 'privacy', 'Bulwark').filter((r) => r.advanced).map((r) => r.label)).not.toContain('Sender favicons');
|
||||
});
|
||||
});
|
||||
|
||||
// settings-8 (2026-11-04): "offline", "autoreply" and "logout" found nothing,
|
||||
// and "lock" found "Time format" (clock) and "Send Checks" (block).
|
||||
describe('settings search: words, word starts and synonyms', () => {
|
||||
/** Every row of every page that the query finds, as "page: label". */
|
||||
const everywhere = (query: string) => (Object.keys(tabSearchEntries) as SettingsSearchTab[]).flatMap((tab) =>
|
||||
settingsSubResults(en, tab, 'Bulwark')
|
||||
.filter((r) => matchesAllWords(subResultText(r), query))
|
||||
.map((r) => `${tab}: ${r.label}`));
|
||||
|
||||
it('matches whole words and the start of words, not the inside of a word', () => {
|
||||
expect(matchesAllWords('App lock', 'lock')).toBe(true);
|
||||
expect(matchesAllWords('Clock format', 'lock')).toBe(false);
|
||||
expect(matchesAllWords('Block senders', 'lock')).toBe(false);
|
||||
expect(matchesAllWords('Notifications', 'notif')).toBe(true);
|
||||
expect(matchesAllWords('Two-factor authentication', 'two factor')).toBe(true);
|
||||
expect(matchesAllWords('Mention recipients', 'pin')).toBe(false);
|
||||
});
|
||||
|
||||
it('still finds words inside Chinese, Japanese and Thai text, which has no spaces', () => {
|
||||
expect(matchesAllWords('自动回复设置', '回复')).toBe(true);
|
||||
expect(matchesAllWords('ตอบกลับอัตโนมัติ', 'อัตโนมัติ')).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['offline', 'offline: Keep mail on this device'],
|
||||
['autoreply', 'vacation: Reply automatically'],
|
||||
['out of office', 'vacation: Reply automatically'],
|
||||
['vacation', 'vacation: Reply automatically'],
|
||||
['logout', 'account: Sign out'],
|
||||
['sign out', 'account: Sign out'],
|
||||
['2fa', 'security: TOTP authentication'],
|
||||
['two-factor', 'security: TOTP authentication'],
|
||||
['password', 'security: Change password'],
|
||||
['signature', 'composing: Signatures'],
|
||||
['font size', 'accessibility: Text size'],
|
||||
['outbox', 'offline: Outbox'],
|
||||
['s/mime', 'security: Encryption'],
|
||||
])('finds "%s"', (query, row) => {
|
||||
expect(everywhere(query)).toContain(row);
|
||||
});
|
||||
|
||||
it('finds the app lock for "pin", and nothing that only has "pin" inside a word', () => {
|
||||
expect(everywhere('pin')).toEqual(['general: Lock with a PIN']);
|
||||
});
|
||||
|
||||
it('finds the Privacy page by its name and keywords', () => {
|
||||
expect(matchesAllWords(`Privacy ${tabKeywords.privacy}`, 'privacy')).toBe(true);
|
||||
expect(matchesAllWords(`Privacy ${tabKeywords.privacy}`, 'tracking')).toBe(true);
|
||||
expect(matchesAllWords(tabKeywords.reading, 'privacy')).toBe(false);
|
||||
});
|
||||
|
||||
it('finds the app lock for "lock", not the clock or blocked senders', () => {
|
||||
const found = everywhere('lock');
|
||||
expect(found).toContain('general: App lock');
|
||||
// "12-hour or 24-hour clock" and "block" in their descriptions.
|
||||
expect(found).not.toContain('general: Time format');
|
||||
expect(found).not.toContain('privacy: Images & external content');
|
||||
});
|
||||
});
|
||||
|
||||
// Decision 9 (2026-11-04): one setting, one place. Two rows called the same
|
||||
// ("Notification sound" twice on Accessibility) made people change one and
|
||||
// find the other "changed by itself".
|
||||
describe('settings search: no two rows with the same name', () => {
|
||||
// Renamed on other branches of the same fix (settings-27 on the
|
||||
// notification sound and mail rows, the Default view rows of Calendar and
|
||||
// Files still to decide). Empty this list once they're merged.
|
||||
const PENDING_RENAMES = new Set(['email notifications', 'notification sound', 'default view']);
|
||||
|
||||
it('names every indexed row and section differently, across all pages', () => {
|
||||
const seen = new Map<string, string>();
|
||||
const duplicates: string[] = [];
|
||||
for (const [tab, entries] of Object.entries(tabSearchEntries)) {
|
||||
for (const entry of entries) {
|
||||
const text = (withAppName(getByPath(en, entry.label), 'Bulwark') as string).trim().toLowerCase();
|
||||
if (PENDING_RENAMES.has(text)) continue;
|
||||
const where = `${tab}: ${entry.label}`;
|
||||
if (seen.has(text)) duplicates.push(`"${text}" at ${seen.get(text)} and ${where}`);
|
||||
else seen.set(text, where);
|
||||
}
|
||||
}
|
||||
expect(duplicates).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// @vitest-environment node
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
// settings-35 (2026-11-04): "Reply from the address it was sent to" ended
|
||||
// with "…is set by “Received Address Matching” below", a row that only shows
|
||||
// when the admin allows custom From addresses, under another name. The
|
||||
// description says what the switch does and points nowhere.
|
||||
const KEY = ['settings', 'email_behavior', 'auto_select_reply_identity', 'description'];
|
||||
const POINTERS: Record<string, RegExp> = {
|
||||
en: /below|Received Address Matching/i,
|
||||
de: /unten|darunter/i,
|
||||
vi: /bên dưới|Khớp địa chỉ nhận/i,
|
||||
et: /allpool|sobitamine/i,
|
||||
th: /ด้านล่าง/,
|
||||
};
|
||||
|
||||
const localesDir = path.resolve(__dirname, '../../locales');
|
||||
|
||||
function read(locale: string, file: string): unknown {
|
||||
const p = path.join(localesDir, locale, file);
|
||||
return fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf8')) : undefined;
|
||||
}
|
||||
|
||||
function at(obj: unknown, keys: string[]): unknown {
|
||||
return keys.reduce<unknown>((cur, k) => (cur && typeof cur === 'object' ? (cur as Record<string, unknown>)[k] : undefined), obj);
|
||||
}
|
||||
|
||||
describe('reply identity description', () => {
|
||||
it.each(Object.keys(POINTERS))('doesn\'t point to a row below in %s', (locale) => {
|
||||
for (const file of ['common.json', 'plain.json']) {
|
||||
const text = at(read(locale, file), KEY);
|
||||
if (typeof text === 'string') expect(text).not.toMatch(POINTERS[locale]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// settings-36 (2026-11-04): Backup & reset said "JSON", "Upload settings from
|
||||
// JSON file" and "— fixes a stale or wrong view"; the offline storage note's
|
||||
// button said "Ask again" without saying what it asks.
|
||||
describe('backup and offline wording', () => {
|
||||
const locales = fs.readdirSync(localesDir).filter((l) => fs.statSync(path.join(localesDir, l)).isDirectory());
|
||||
|
||||
it.each(locales)('%s: no JSON in the settings file rows, no dash aside in Refresh', (locale) => {
|
||||
for (const file of ['common.json', 'plain.json']) {
|
||||
const messages = read(locale, file);
|
||||
for (const row of ['export_settings', 'import_settings']) {
|
||||
const text = at(messages, ['settings', 'advanced', row, 'description']);
|
||||
if (typeof text === 'string') expect(text).not.toMatch(/json/i);
|
||||
}
|
||||
const refresh = at(messages, ['settings', 'advanced', 'refresh_cache', 'description']);
|
||||
if (typeof refresh === 'string') expect(refresh).not.toMatch(/—/);
|
||||
}
|
||||
});
|
||||
|
||||
it('says what the storage button asks for', () => {
|
||||
expect(at(read('en', 'common.json'), ['offlineStorage', 'persist_retry'])).toBe('Ask the browser to keep it');
|
||||
});
|
||||
});
|
||||
@@ -43,7 +43,7 @@ const SETTINGS_TABS: readonly (SettingsSearchTab | typeof LEGACY_SETTINGS_TABS[n
|
||||
'account', 'general', 'notifications', 'appearance', 'message_list', 'reading',
|
||||
'composing', 'vacation', 'filters',
|
||||
'organize', 'security', 'calendar', 'contacts',
|
||||
'files', 'addons', 'backup',
|
||||
'files', 'addons', 'offline', 'privacy', 'backup', 'about',
|
||||
...LEGACY_SETTINGS_TABS,
|
||||
];
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { settingsHooks } from '../../plugin-hooks';
|
||||
import { noThirdPartyTraffic } from '../../third-party-mode';
|
||||
import { toastDurationAtLeast } from '../../accessibility-settings';
|
||||
import { detectBrowserLocale } from '@/i18n/detect-locale';
|
||||
import { getByPath } from '../../settings-search';
|
||||
import { getByPath, settingsTabLabelKey } from '../../settings-search';
|
||||
import {
|
||||
DENIED_SETTINGS, PAGE_GATES, SETTINGS_SCHEMA, isAllowedSettingValue, settingValueLabelKey,
|
||||
type SettingSchemaEntry,
|
||||
@@ -195,7 +195,7 @@ export async function doDescribeSettings(arg: unknown): Promise<Record<string, S
|
||||
key,
|
||||
label: format(messages, entry.label) ?? key,
|
||||
page: entry.page,
|
||||
pageLabel: format(messages, `settings.tabs.${entry.page}`) ?? entry.page,
|
||||
pageLabel: format(messages, settingsTabLabelKey(entry.page)) ?? entry.page,
|
||||
current,
|
||||
currentLabel: settingValueLabel(entry, current, messages, locale),
|
||||
...(next !== undefined ? { next: next as SettingValue, nextLabel: settingValueLabel(entry, next, messages, locale) } : {}),
|
||||
|
||||
@@ -15,8 +15,9 @@ import { useAuthStore } from '@/stores/auth-store';
|
||||
import { usePolicyStore } from '@/stores/policy-store';
|
||||
import { fetchConfig, type ConfigData } from '@/hooks/use-config';
|
||||
import { productName } from '@/lib/product-name';
|
||||
import { IS_LITE } from '@/lib/lite';
|
||||
import {
|
||||
getByPath, settingsSubResults, tabKeywords, tabSearchEntries, type SettingsSearchTab,
|
||||
getByPath, settingsSubResults, settingsTabLabelKey, tabKeywords, tabSearchEntries, type SettingsSearchTab,
|
||||
} from '@/lib/settings-search';
|
||||
import { currentSessionLimits } from '@/lib/session-policy/limits';
|
||||
import type { AppSettingsView, SettingsSearchResult } from '../plugin-types';
|
||||
@@ -44,7 +45,8 @@ function availablePages(config: ConfigData | null): Set<SettingsSearchTab> {
|
||||
try { return policy.isFeatureEnabled(feature); } catch { return true; }
|
||||
};
|
||||
const stalwart = config?.stalwartFeaturesEnabled ?? true;
|
||||
const pages = new Set<SettingsSearchTab>(['account', 'notifications', 'general', 'appearance', 'message_list', 'reading', 'composing', 'organize', 'backup']);
|
||||
const pages = new Set<SettingsSearchTab>(['account', 'privacy', 'notifications', 'general', 'appearance', 'message_list', 'reading', 'composing', 'organize', 'backup', 'about']);
|
||||
if (!IS_LITE) pages.add('offline');
|
||||
if (stalwart) pages.add('security');
|
||||
if (client?.supportsSieve?.()) pages.add('filters');
|
||||
if (client?.supportsVacationResponse?.()) pages.add('vacation');
|
||||
@@ -56,7 +58,7 @@ function availablePages(config: ConfigData | null): Set<SettingsSearchTab> {
|
||||
}
|
||||
|
||||
function pageName(messages: Messages, tab: SettingsSearchTab): string {
|
||||
const name = getByPath(messages, `settings.tabs.${tab}`);
|
||||
const name = getByPath(messages, settingsTabLabelKey(tab));
|
||||
return typeof name === 'string' ? name : tab;
|
||||
}
|
||||
|
||||
@@ -91,7 +93,7 @@ export async function doAppSearchSettings(query: unknown, options?: unknown): Pr
|
||||
const theirs = settingsSubResults(english, tab, appName);
|
||||
mine.forEach((entry, i) => {
|
||||
const en = theirs[i];
|
||||
const text = `${entry.label} ${entry.description ?? ''} ${en?.label ?? ''} ${en?.description ?? ''}`.toLowerCase();
|
||||
const text = `${entry.label} ${entry.description ?? ''} ${entry.keywords ?? ''} ${en?.label ?? ''} ${en?.description ?? ''} ${en?.keywords ?? ''}`.toLowerCase();
|
||||
let score = 0;
|
||||
for (const word of words) {
|
||||
if (text.includes(word)) score += 2;
|
||||
|
||||
@@ -215,8 +215,8 @@ const ENTRIES: SettingSchemaEntry[] = [
|
||||
bool('tintListRowsByTag', 'message_list', `${AP}tint_list_rows.label`),
|
||||
bool('showVerificationCodes', 'message_list', `${EB}show_verification_codes.label`),
|
||||
// Both make the server fetch the sender's domain: the assistant may only switch them off.
|
||||
bool('senderFavicons', 'message_list', 'settings.advanced.sender_favicons.label', { thirdParty: true, only: false }),
|
||||
bool('showAvatarsInJunk', 'message_list', 'settings.advanced.show_avatars_in_junk.label', { thirdParty: true, only: false }),
|
||||
bool('senderFavicons', 'privacy', 'settings.advanced.sender_favicons.label', { thirdParty: true, only: false }),
|
||||
bool('showAvatarsInJunk', 'privacy', 'settings.advanced.show_avatars_in_junk.label', { thirdParty: true, only: false }),
|
||||
entry('hoverActions', 'message_list', `${EB}hover_actions.label`, {
|
||||
type: 'list',
|
||||
options: HOVER_ACTION_IDS.map(([value, labelKey]) => ({
|
||||
@@ -295,9 +295,9 @@ const ENTRIES: SettingSchemaEntry[] = [
|
||||
{ type: 'words', max: 50, maxLength: 40, lowercase: true }),
|
||||
bool('emptySubjectWarningEnabled', 'composing', `${EB}empty_subject_warning.label`),
|
||||
bool('placeholderWarningEnabled', 'composing', `${EB}placeholder_warning.label`),
|
||||
bool('requestReadReceiptDefault', 'composing', `${EB}request_read_receipt.label`),
|
||||
bool('requestReadReceiptDefault', 'privacy', `${EB}request_read_receipt.label`),
|
||||
// No "always": that sends a receipt mail by itself.
|
||||
choice('readReceiptResponse', 'composing', `${EB}read_receipt_response.label`,
|
||||
choice('readReceiptResponse', 'privacy', `${EB}read_receipt_response.label`,
|
||||
opts(`${EB}read_receipt_response.`, ['ask', 'never'])),
|
||||
|
||||
// --- Notifications ---
|
||||
|
||||
+95
-40
@@ -21,7 +21,10 @@ export type SettingsSearchTab =
|
||||
| 'contacts'
|
||||
| 'files'
|
||||
| 'addons'
|
||||
| 'backup';
|
||||
| 'offline'
|
||||
| 'privacy'
|
||||
| 'backup'
|
||||
| 'about';
|
||||
|
||||
type Tab = SettingsSearchTab;
|
||||
|
||||
@@ -45,8 +48,16 @@ export interface SettingsSearchEntry {
|
||||
advanced?: true;
|
||||
/** The page draws the row only then. */
|
||||
when?: SettingsSearchCondition;
|
||||
/**
|
||||
* Translation key of other words people use for this setting (synonyms,
|
||||
* abbreviations), in the user's language. Searched like the label.
|
||||
*/
|
||||
synonyms?: string;
|
||||
}
|
||||
|
||||
/** Key of a row's synonyms in the locale files. */
|
||||
const syn = (id: string) => `uxs_structure.search.${id}`;
|
||||
|
||||
// The settings each page draws, as the search lists them. Only labels a page
|
||||
// renders belong here: settings-search-anchors.test.tsx renders every page
|
||||
// and fails for an entry without its row. A description is found next to
|
||||
@@ -57,11 +68,12 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
|
||||
{ label: 'settings.account.email.label' },
|
||||
{ label: 'settings.account.auth_method_label' },
|
||||
{ label: 'settings.account.server.label', advanced: true },
|
||||
{ label: 'settings.account.sections.addresses' },
|
||||
{ label: 'settings.account.sections.addresses', synonyms: syn('signature') },
|
||||
{ label: 'settings.identities.identities_count.label' },
|
||||
{ label: 'settings.identities.sub_addressing.label' },
|
||||
{ label: 'settings.email_behavior.sub_address_delimiter.label', advanced: true },
|
||||
{ label: 'settings.account.accounts.title', when: 'multiAccount' },
|
||||
{ label: 'sidebar.sign_out', synonyms: syn('sign_out') },
|
||||
],
|
||||
general: [
|
||||
{ label: 'settings.language_region.title' },
|
||||
@@ -72,7 +84,7 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
|
||||
{ label: 'settings.language_region.time_zone.label' },
|
||||
{ label: 'settings.language_region.first_day.label' },
|
||||
{ label: 'appLock.section_title' },
|
||||
{ label: 'appLock.enable_label' },
|
||||
{ label: 'appLock.enable_label', synonyms: syn('app_lock') },
|
||||
{ label: 'protocol_handlers.title' },
|
||||
{ label: 'protocol_handlers.mailto_label' },
|
||||
{ label: 'protocol_handlers.webcal_label' },
|
||||
@@ -116,7 +128,7 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
|
||||
{ label: 'settings.accessibility.presets.title' },
|
||||
{ label: 'settings.accessibility.quick_button.label' },
|
||||
{ label: 'settings.accessibility.sections.text' },
|
||||
{ label: 'settings.accessibility.text_size.label' },
|
||||
{ label: 'settings.accessibility.text_size.label', synonyms: syn('text_size') },
|
||||
{ label: 'settings.accessibility.font.label' },
|
||||
{ label: 'settings.accessibility.plain_text_font.label' },
|
||||
{ label: 'settings.accessibility.line_spacing.label' },
|
||||
@@ -126,15 +138,12 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
|
||||
{ label: 'settings.accessibility.reading_width.label' },
|
||||
{ label: 'settings.accessibility.reading_mask.label' },
|
||||
{ label: 'settings.accessibility.plain_language.label' },
|
||||
{ label: 'settings.appearance.list_density.label' },
|
||||
{ label: 'settings.appearance.toolbar_labels.label' },
|
||||
{ label: 'settings.accessibility.sections.contrast' },
|
||||
{ label: 'settings.accessibility.contrast.label' },
|
||||
{ label: 'settings.accessibility.transparency.label' },
|
||||
{ label: 'settings.accessibility.underline_links.label' },
|
||||
{ label: 'settings.accessibility.color_names.label' },
|
||||
{ label: 'settings.accessibility.cvd_palette.label' },
|
||||
{ label: 'settings.email_behavior.always_light_mode.label' },
|
||||
{ label: 'settings.accessibility.theme_link.label' },
|
||||
{ label: 'settings.accessibility.sections.motion' },
|
||||
{ label: 'settings.accessibility.reduce_motion.label' },
|
||||
@@ -148,7 +157,6 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
|
||||
{ label: 'settings.accessibility.large_targets.label' },
|
||||
{ label: 'settings.accessibility.large_cursor.label' },
|
||||
{ label: 'settings.accessibility.open_on_hover.label' },
|
||||
{ label: 'calendar.settings.hover_preview' },
|
||||
{ label: 'settings.accessibility.row_action_button.label' },
|
||||
{ label: 'settings.accessibility.native_scrollbars.label' },
|
||||
{ label: 'settings.accessibility.row_actions_link.label' },
|
||||
@@ -161,24 +169,14 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
|
||||
{ label: 'settings.accessibility.read_aloud_rate.label' },
|
||||
{ label: 'settings.accessibility.compose_font.label' },
|
||||
{ label: 'settings.accessibility.compose_size.label' },
|
||||
{ label: 'settings.email_behavior.group_conversations.label' },
|
||||
{ label: 'settings.email_behavior.show_preview.label' },
|
||||
{ label: 'settings.email_behavior.mark_read.label' },
|
||||
{ label: 'settings.email_behavior.return_to_list_after_action.label' },
|
||||
{ label: 'settings.accessibility.sections.timing' },
|
||||
{ label: 'settings.accessibility.toast_duration.label' },
|
||||
{ label: 'settings.email_behavior.send_delay.label' },
|
||||
{ label: 'settings.accessibility.confirm_send.label' },
|
||||
{ label: 'settings.accessibility.confirm_delete.label' },
|
||||
{ label: 'settings.accessibility.confirm_archive.label' },
|
||||
{ label: 'settings.email_behavior.confirm_bulk_delete.label' },
|
||||
{ label: 'settings.accessibility.send_warnings_link.label' },
|
||||
{ label: 'settings.accessibility.sections.alerts' },
|
||||
{ label: 'settings.accessibility.visual_alerts.label' },
|
||||
{ label: 'settings.notifications.email.sound' },
|
||||
{ label: 'settings.notifications.calendar.sound' },
|
||||
{ label: 'settings.notifications.sound_selection.choose' },
|
||||
{ label: 'settings.appearance.favicon_unread_badge.label' },
|
||||
{ label: 'settings.accessibility.unread_counts.label' },
|
||||
{ label: 'settings.accessibility.do_not_disturb.label' },
|
||||
{ label: 'settings.accessibility.sections.screen_readers' },
|
||||
@@ -195,10 +193,6 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
|
||||
{ label: 'settings.appearance.message_list_order.scope.label' },
|
||||
{ label: 'settings.appearance.tint_list_rows.label' },
|
||||
{ label: 'settings.email_behavior.show_verification_codes.label' },
|
||||
{ label: 'settings.message_list.sections.senders' },
|
||||
{ label: 'settings.advanced.sender_favicons.label' },
|
||||
{ label: 'settings.advanced.sender_favicons.refresh_label' },
|
||||
{ label: 'settings.advanced.show_avatars_in_junk.label', advanced: true },
|
||||
{ label: 'settings.message_list.sections.actions' },
|
||||
{ label: 'settings.email_behavior.hover_actions.label' },
|
||||
{ label: 'settings.message_list.sections.delete' },
|
||||
@@ -221,10 +215,6 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
|
||||
{ label: 'settings.email_behavior.message_spacing.label' },
|
||||
{ label: 'settings.reading_page.plain_text_font_link.label' },
|
||||
{ label: 'settings.email_behavior.always_light_mode.label' },
|
||||
{ label: 'settings.reading_page.sections.images' },
|
||||
{ label: 'settings.email_behavior.external_content.label' },
|
||||
{ label: 'settings.email_behavior.trusted_senders.label' },
|
||||
{ label: 'settings.email_behavior.trusted_senders.use_address_book_label' },
|
||||
{ label: 'settings.reading_page.sections.attachments' },
|
||||
{ label: 'settings.email_behavior.attachment_position.label' },
|
||||
{ label: 'settings.email_behavior.attachment_click_action.label' },
|
||||
@@ -260,18 +250,15 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
|
||||
{ label: 'settings.email_behavior.attachment_reminder.label' },
|
||||
{ label: 'settings.email_behavior.empty_subject_warning.label' },
|
||||
{ label: 'settings.email_behavior.placeholder_warning.label' },
|
||||
{ label: 'settings.composing_page.sections.receipts' },
|
||||
{ label: 'settings.email_behavior.request_read_receipt.label' },
|
||||
{ label: 'settings.email_behavior.read_receipt_response.label' },
|
||||
{ label: 'settings.templates.title' },
|
||||
{ label: 'settings.templates.export_import', advanced: true },
|
||||
{ label: 'settings.email_behavior.signatures_link.label' },
|
||||
{ label: 'settings.email_behavior.signatures_link.label', synonyms: syn('signature') },
|
||||
],
|
||||
vacation: [
|
||||
{ label: 'uxs_vacation.from_label' },
|
||||
{ label: 'uxs_vacation.until_label' },
|
||||
{ label: 'uxs_vacation.message_title' },
|
||||
{ label: 'settings.vacation.status.label' },
|
||||
{ label: 'settings.vacation.status.label', synonyms: syn('vacation') },
|
||||
{ label: 'settings.vacation.message.subject_label' },
|
||||
{ label: 'uxs_vacation.message_label' },
|
||||
{ label: 'settings.vacation.forward.title' },
|
||||
@@ -296,10 +283,10 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
|
||||
],
|
||||
security: [
|
||||
{ label: 'settings.security.title' },
|
||||
{ label: 'settings.security.totp.label' },
|
||||
{ label: 'settings.security.totp.label', synonyms: syn('two_factor') },
|
||||
{ label: 'settings.security.sections.devices' },
|
||||
{ label: 'settings.security.sections.encryption', advanced: true },
|
||||
{ label: 'settings.security.password.title' },
|
||||
{ label: 'settings.security.sections.encryption', advanced: true, synonyms: syn('encryption') },
|
||||
{ label: 'settings.security.password.title', synonyms: syn('password') },
|
||||
{ label: 'settings.security.totp.section_title' },
|
||||
{ label: 'settings.security.app_passwords.title' },
|
||||
{ label: 'settings.security.api_keys.title', advanced: true },
|
||||
@@ -357,17 +344,56 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
|
||||
{ label: 'settings.sidebar_apps.title' },
|
||||
{ label: 'settings.sidebar_apps.keep_loaded', advanced: true },
|
||||
],
|
||||
offline: [
|
||||
{ label: 'offline.settings.title' },
|
||||
{ label: 'offline.settings.keep_label', synonyms: syn('offline') },
|
||||
{ label: 'offlineWipe.settings_title', synonyms: syn('wipe') },
|
||||
{ label: 'offline.outbox.title', synonyms: syn('outbox') },
|
||||
],
|
||||
privacy: [
|
||||
{ label: 'settings.reading_page.sections.images', synonyms: syn('remote_content') },
|
||||
{ label: 'settings.email_behavior.external_content.label' },
|
||||
{ label: 'settings.email_behavior.trusted_senders.label' },
|
||||
{ label: 'settings.email_behavior.trusted_senders.use_address_book_label' },
|
||||
{ label: 'settings.composing_page.sections.receipts' },
|
||||
{ label: 'settings.email_behavior.request_read_receipt.label' },
|
||||
{ label: 'settings.email_behavior.read_receipt_response.label' },
|
||||
{ label: 'settings.message_list.sections.senders' },
|
||||
{ label: 'settings.advanced.sender_favicons.label' },
|
||||
{ label: 'settings.advanced.sender_favicons.refresh_label' },
|
||||
{ label: 'settings.advanced.show_avatars_in_junk.label', advanced: true },
|
||||
{ label: 'uxs_structure.privacy.push_title' },
|
||||
{ label: 'uxs_structure.privacy.push_link.label', synonyms: syn('push') },
|
||||
],
|
||||
backup: [
|
||||
{ label: 'settings.advanced.sections.backup' },
|
||||
{ label: 'settings.advanced.export_settings.label' },
|
||||
{ label: 'settings.advanced.import_settings.label' },
|
||||
{ label: 'mail_import.section_title' },
|
||||
{ label: 'mail_import.settings_label' },
|
||||
{ label: 'mail_export.settings_label' },
|
||||
{ label: 'settings.advanced.sections.fix' },
|
||||
{ label: 'settings.advanced.refresh_cache.label' },
|
||||
{ label: 'settings.advanced.reset_settings.label' },
|
||||
{ label: 'settings.advanced.debug_mode.label', advanced: true },
|
||||
],
|
||||
about: [
|
||||
{ label: 'uxs_structure.about.title', synonyms: syn('version') },
|
||||
],
|
||||
};
|
||||
|
||||
/** Pages added after `settings.tabs` keep their names in their own namespace. */
|
||||
const NEW_TAB_LABELS: Partial<Record<Tab, string>> = {
|
||||
offline: 'uxs_structure.tabs.offline',
|
||||
privacy: 'uxs_structure.tabs.privacy',
|
||||
about: 'uxs_structure.tabs.about',
|
||||
};
|
||||
|
||||
/** Translation key of a settings page's name. */
|
||||
export function settingsTabLabelKey(tab: SettingsSearchTab): string {
|
||||
return NEW_TAB_LABELS[tab] ?? `settings.tabs.${tab}`;
|
||||
}
|
||||
|
||||
// Extra English keywords per tab so common search terms hit even when the
|
||||
// translation doesn't contain the literal word.
|
||||
/** Settings shown only while the admin has the Archive action on (#1257). */
|
||||
@@ -380,7 +406,7 @@ export const tabKeywords: Record<Tab, string> = {
|
||||
appearance: 'theme dark light font size accent color animation density custom theme css skin toolbar sidebar account switcher icons rail layout reading pane',
|
||||
accessibility: 'a11y accessible vision blind low vision screen reader contrast motion animation reduce zoom text size font dyslexia spacing keyboard shortcuts focus outline cursor pointer targets colour blind color sound visual alert hearing deaf simple plain language read aloud',
|
||||
message_list: 'inbox list preview thread conversation grouping sort order columns archive delete swipe hover favicon avatar tag color',
|
||||
reading: 'mark read attachment open monospace mono font plain text images remote content block sender privacy tracking dark light download filename template eml save export',
|
||||
reading: 'mark read attachment open monospace mono font plain text dark light download filename template eml save export',
|
||||
composing: 'editor signature plain text reply forward draft compose template snippet quick reply mention undo send receipt attachment reminder',
|
||||
vacation: 'auto reply away out of office holiday responder',
|
||||
filters: 'sieve rules add create new block junk forward',
|
||||
@@ -392,7 +418,10 @@ export const tabKeywords: Record<Tab, string> = {
|
||||
contacts: 'address book contact',
|
||||
files: 'attachments cloud drive storage upload',
|
||||
addons: 'apps webview iframe plugins extensions addons sidebar',
|
||||
backup: 'export import storage quota privacy backup reset sync cache about version debug logs developer console diagnostic',
|
||||
offline: 'offline connection storage device sync outbox local kept',
|
||||
privacy: 'privacy tracking remote images content read receipts favicons pictures telemetry crash reports',
|
||||
backup: 'export import backup reset sync cache debug logs developer console diagnostic',
|
||||
about: 'about version update source code github license',
|
||||
};
|
||||
|
||||
// Some settings texts name the app with {appName} (#1314). The index keeps
|
||||
@@ -410,6 +439,8 @@ export function withAppName(node: unknown, appName: string): unknown {
|
||||
export interface SubResult {
|
||||
label: string;
|
||||
description?: string;
|
||||
/** Other words for the setting (its synonyms), searched like the label. */
|
||||
keywords?: string;
|
||||
// For plugin setting fields: the id of the plugin whose card needs to be
|
||||
// expanded before the field becomes visible in the DOM.
|
||||
pluginId?: string;
|
||||
@@ -450,20 +481,44 @@ export function settingsSubResults(
|
||||
if (typeof label !== 'string') continue;
|
||||
const descriptionKey = descriptionKeyFor(entry.label, messages);
|
||||
const description = descriptionKey ? (getByPath(messages, descriptionKey) as string) : undefined;
|
||||
const keywords = entry.synonyms ? getByPath(messages, entry.synonyms) : undefined;
|
||||
list.push({
|
||||
label: withAppName(label, appName) as string,
|
||||
...(description ? { description: withAppName(description, appName) as string } : {}),
|
||||
...(typeof keywords === 'string' && keywords ? { keywords } : {}),
|
||||
...(entry.advanced ? { advanced: true } : {}),
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/** Every word of the query is somewhere in the text, in any order. */
|
||||
// Scripts written without spaces between words: a query word there can
|
||||
// only be found inside the text, not at the start of a word.
|
||||
const NO_WORD_BREAKS = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Thai}]/u;
|
||||
|
||||
/** The words of a text or query: letters and digits, lower case. */
|
||||
export function searchWords(text: string): string[] {
|
||||
return text.toLowerCase().split(/[^\p{L}\p{N}\p{M}]+/u).filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Every word of the query starts a word of the text, in any order ("lock"
|
||||
* finds "App lock", not "Clock" or "Block"). In Chinese, Japanese and Thai,
|
||||
* which don't put spaces between words, a query word may sit anywhere.
|
||||
*/
|
||||
export function matchesAllWords(text: string, query: string): boolean {
|
||||
const haystack = text.toLowerCase();
|
||||
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
||||
return words.length > 0 && words.every((word) => haystack.includes(word));
|
||||
const words = searchWords(query);
|
||||
if (words.length === 0) return false;
|
||||
const haystack = searchWords(text);
|
||||
const flat = text.toLowerCase();
|
||||
return words.every((word) => (
|
||||
NO_WORD_BREAKS.test(word) ? flat.includes(word) : haystack.some((candidate) => candidate.startsWith(word))
|
||||
));
|
||||
}
|
||||
|
||||
/** The text a row is found by: its name, what it says under the name, and its synonyms. */
|
||||
export function subResultText(result: SubResult): string {
|
||||
return [result.label, result.description ?? '', result.keywords ?? ''].join(' ');
|
||||
}
|
||||
|
||||
export function getByPath(obj: unknown, path: string): unknown {
|
||||
|
||||
@@ -8,7 +8,7 @@ export const SETTINGS_TAB_IDS: readonly SettingsSearchTab[] = [
|
||||
'account', 'general', 'notifications', 'appearance', 'accessibility',
|
||||
'message_list', 'reading', 'composing', 'vacation', 'filters', 'organize',
|
||||
'security', 'sharing', 'recovery', 'calendar', 'contacts', 'files',
|
||||
'addons', 'backup',
|
||||
'addons', 'offline', 'privacy', 'backup', 'about',
|
||||
];
|
||||
|
||||
// Map legacy tab IDs to current ones; runs once on read of localStorage and
|
||||
@@ -20,7 +20,7 @@ export const LEGACY_TAB_MAP: Record<string, SettingsSearchTab> = {
|
||||
advanced: 'backup',
|
||||
layout: 'appearance',
|
||||
themes: 'appearance',
|
||||
content_senders: 'reading',
|
||||
content_senders: 'privacy',
|
||||
identities: 'account',
|
||||
language: 'general',
|
||||
protocol_handlers: 'general',
|
||||
|
||||
+80
-5
@@ -1988,7 +1988,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "الرد من العنوان المستلَم إليه",
|
||||
"description": "عند الرد، الإرسال من العنوان الذي أُرسلت إليه الرسالة أصلًا. يطابق الهويات أولًا؛ وبالنسبة لعمليات التسليم الشاملة للنطاق، يعيد كتابة ترويسة \"من\" إلى العنوان البديل مع الإرسال عبر هويتك الأساسية."
|
||||
"description": "عند الرد، الإرسال من العنوان الذي أُرسلت إليه الرسالة."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "مطابقة عنوان الاستلام",
|
||||
@@ -2636,7 +2636,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "تحديث البيانات المخزنة مؤقتًا",
|
||||
"description": "إعادة تحميل جهات الاتصال والتقويمات والمجلدات من الخادم. يحافظ على حساباتك وجلساتك — يصلح العرض القديم أو الخاطئ دون تسجيل الخروج.",
|
||||
"description": "إعادة تحميل جهات الاتصال والتقويمات والمجلدات من الخادم. تبقى مسجلًا للدخول.",
|
||||
"button": "تحديث",
|
||||
"confirm_title": "تحديث البيانات المخزنة مؤقتًا؟"
|
||||
},
|
||||
@@ -2649,12 +2649,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "تصدير الإعدادات",
|
||||
"description": "تنزيل إعداداتك بصيغة JSON",
|
||||
"description": "تنزيل نسخة من إعداداتك",
|
||||
"button": "تصدير"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "استيراد الإعدادات",
|
||||
"description": "رفع الإعدادات من ملف JSON",
|
||||
"description": "استعادة الإعدادات من ملف",
|
||||
"button": "استيراد"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "دون اتصال",
|
||||
"privacy": "الخصوصية",
|
||||
"about": "حول"
|
||||
},
|
||||
"search": {
|
||||
"signature": "توقيع توقيع البريد تذييل",
|
||||
"sign_out": "تسجيل الخروج خروج logout",
|
||||
"app_lock": "PIN قفل رمز شاشة القفل",
|
||||
"text_size": "حجم الخط نص أكبر تكبير",
|
||||
"vacation": "رد تلقائي خارج المكتب غائب إجازة عطلة autoreply",
|
||||
"two_factor": "2FA ثنائي العوامل خطوتين MFA OTP المصادقة رمز لمرة واحدة",
|
||||
"encryption": "S/MIME smime PGP OpenPGP تشفير شهادة",
|
||||
"password": "كلمة المرور تغيير كلمة المرور كلمة مرور جديدة",
|
||||
"offline": "دون اتصال offline بلا إنترنت تنزيل تخزين مزامنة",
|
||||
"wipe": "إزالة حذف مسح الجهاز البيانات",
|
||||
"outbox": "صندوق الصادر غير مرسل في الانتظار قائمة",
|
||||
"remote_content": "صور بعيدة محتوى خارجي تتبع بكسلات مرسلون موثوقون",
|
||||
"push": "push مرحل الخلفية إشعارات",
|
||||
"version": "إصدار تحديث حول الشيفرة المصدرية GitHub ترخيص"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "الإشعارات",
|
||||
"push_link": {
|
||||
"label": "مرحّل الإشعارات",
|
||||
"description": "تمر الإشعارات أثناء إغلاق التطبيق عبر مرحّل دفع لا يرى بريدك أبدًا. تختاره في صفحة الإشعارات.",
|
||||
"button": "الإشعارات"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "الصور البعيدة والمرسلون الموثوقون",
|
||||
"receipts_label": "إيصالات القراءة",
|
||||
"senders_label": "صور المرسلين",
|
||||
"description": "هذه الإعدادات موجودة في صفحة الخصوصية.",
|
||||
"button": "الخصوصية"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "الكثافة وتسميات الأزرار",
|
||||
"description": "تضبط تقارب صفوف القائمة وإظهار أسماء الأزرار في صفحة المظهر."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "خلفية فاتحة للرسائل",
|
||||
"description": "تضبط عرض الرسائل على خلفية فاتحة في الوضع الداكن في صفحة القراءة."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "معاينة الأحداث في التقويم",
|
||||
"description": "تضبط إظهار تفاصيل الحدث عند الإشارة إليه في صفحة التقويم."
|
||||
},
|
||||
"list": {
|
||||
"label": "المحادثات وأسطر المعاينة",
|
||||
"description": "تضبط التجميع حسب المحادثة وسطر المعاينة تحت كل رسالة في صفحة قائمة الرسائل."
|
||||
},
|
||||
"reading": {
|
||||
"label": "التعليم كمقروء",
|
||||
"description": "تضبط متى تُعد الرسالة مقروءة وإلى أين تنتقل بعد حذفها أو نقلها في صفحة القراءة."
|
||||
},
|
||||
"send_warnings_description": "التراجع عن الإرسال والتحذيرات من موضوع فارغ أو مرفق منسي أو نص مؤقت.",
|
||||
"bulk_delete": {
|
||||
"label": "السؤال قبل حذف عدة رسائل",
|
||||
"description": "يُضبط هذا في صفحة قائمة الرسائل."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "الأصوات وعداد غير المقروء",
|
||||
"description": "تضبط أصوات الإشعارات وعدد غير المقروء في علامة التبويب في صفحة الإشعارات."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "حول {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "تسجيل الخروج من هذا الحساب على هذا الجهاز."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "إعداد Bulwark Webmail",
|
||||
"subtitle": "اضبط نسخة البريد الإلكتروني على الويب من المتصفح.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, zero {لن تُفقد أي رسالة لم تُرسل.} one {ستُفقد رسالة واحدة لم تُرسل.} two {ستُفقد رسالتان لم تُرسلا.} few {ستُفقد # رسائل لم تُرسل.} many {ستُفقد # رسالة لم تُرسل.} other {ستُفقد # رسالة لم تُرسل.}}",
|
||||
"clear_confirm": "مسح",
|
||||
"persist_refused": "قد يحذف هذا المتصفح البريد المحفوظ عندما تقل المساحة. تثبيت التطبيق يمنع ذلك عادةً.",
|
||||
"persist_retry": "اسأل مرة أخرى",
|
||||
"persist_retry": "اطلب من المتصفح الاحتفاظ بها",
|
||||
"safari_hint": "يحذف Safari بيانات الموقع بعد 7 أيام دون زيارة. أضف Bulwark إلى الشاشة الرئيسية أو إلى Dock لتبقى رسائلك على هذا الجهاز.",
|
||||
"evicted": "حذف المتصفح البريد المحفوظ على هذا الجهاز. يجري تنزيله من جديد.",
|
||||
"evicted_lost": "{count, plural, zero {لم تُفقد أي رسالة أو تغيير لم يُرسل.} one {فُقدت رسالة أو تغيير واحد لم يُرسل.} two {فُقدت رسالتان أو تغييران لم يُرسلا.} few {فُقدت # رسائل أو تغييرات لم تُرسل.} many {فُقدت # رسالة أو تغييرًا لم يُرسل.} other {فُقدت # رسالة أو تغيير لم يُرسل.}}",
|
||||
|
||||
+80
-5
@@ -1988,7 +1988,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Respon des de l'adreça de recepció",
|
||||
"description": "En respondre, envia des de l'adreça a la qual es va enviar originalment el missatge. Primer intenta coincidir amb les identitats; per als lliuraments de captura general de domini, reescriu la capçalera «De» amb l'àlies mentre envia a través de la identitat principal."
|
||||
"description": "En respondre, envia des de l’adreça a la qual es va enviar el missatge."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Coincidència de l'adreça de recepció",
|
||||
@@ -2636,7 +2636,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Actualitza les dades emmagatzemades",
|
||||
"description": "Torna a carregar els contactes, els calendaris i les carpetes des del servidor. Manté els comptes i les sessions — soluciona una vista obsoleta o incorrecta sense tancar la sessió.",
|
||||
"description": "Torna a carregar els contactes, els calendaris i les carpetes des del servidor. Mantens la sessió iniciada.",
|
||||
"button": "Actualitza",
|
||||
"confirm_title": "Voleu actualitzar les dades de la memòria cau?"
|
||||
},
|
||||
@@ -2649,12 +2649,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Exporta la configuració",
|
||||
"description": "Baixeu la configuració en format JSON",
|
||||
"description": "Baixa una còpia dels paràmetres",
|
||||
"button": "Exporta"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Importa la configuració",
|
||||
"description": "Carregueu la configuració des d'un fitxer JSON",
|
||||
"description": "Restaura els paràmetres des d’un fitxer",
|
||||
"button": "Importa"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Fora de línia",
|
||||
"privacy": "Privadesa",
|
||||
"about": "Quant a"
|
||||
},
|
||||
"search": {
|
||||
"signature": "signatura signatura de correu peu de missatge",
|
||||
"sign_out": "tancar la sessió sortir desconnectar logout",
|
||||
"app_lock": "PIN bloqueig codi bloquejar pantalla",
|
||||
"text_size": "mida de la lletra text més gran zoom ampliar",
|
||||
"vacation": "resposta automàtica fora de l’oficina absent vacances autoreply",
|
||||
"two_factor": "2FA dos factors dos passos MFA OTP autenticador codi d’un sol ús",
|
||||
"encryption": "S/MIME smime PGP OpenPGP xifratge certificat",
|
||||
"password": "contrasenya canviar contrasenya nova contrasenya",
|
||||
"offline": "fora de línia sense connexió sense internet baixar emmagatzematge sincronitzar",
|
||||
"wipe": "treure esborrar eliminar dispositiu dades",
|
||||
"outbox": "safata de sortida no enviat en espera cua",
|
||||
"remote_content": "imatges remotes contingut extern seguiment píxels remitents de confiança",
|
||||
"push": "push repetidor segon pla notificacions",
|
||||
"version": "versió actualització quant a codi font GitHub llicència"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Notificacions",
|
||||
"push_link": {
|
||||
"label": "Repetidor de notificacions",
|
||||
"description": "Les notificacions amb l’aplicació tancada passen per un repetidor push, que mai no veu el vostre correu. El trieu a la pàgina Notificacions.",
|
||||
"button": "Notificacions"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Imatges remotes i remitents de confiança",
|
||||
"receipts_label": "Confirmacions de lectura",
|
||||
"senders_label": "Imatges dels remitents",
|
||||
"description": "Aquests paràmetres són a la pàgina Privadesa.",
|
||||
"button": "Privadesa"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Densitat i etiquetes de la barra",
|
||||
"description": "La separació de les files de la llista i si els botons mostren el nom es configuren a la pàgina Aparença."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Fons clar per als missatges",
|
||||
"description": "Mostrar els missatges amb fons clar en mode fosc es configura a la pàgina Lectura."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Previsualització d’esdeveniments al calendari",
|
||||
"description": "Si en assenyalar un esdeveniment se’n mostren els detalls es configura a la pàgina Calendari."
|
||||
},
|
||||
"list": {
|
||||
"label": "Converses i línies de previsualització",
|
||||
"description": "L’agrupació per conversa i la línia de previsualització de cada missatge es configuren a la pàgina Llista de missatges."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Marcar com a llegit",
|
||||
"description": "Quan un missatge compta com a llegit, i on aneu després d’esborrar-lo o moure’l, es configura a la pàgina Lectura."
|
||||
},
|
||||
"send_warnings_description": "Desfer l’enviament i els avisos d’assumpte buit, adjunt oblidat o text provisional.",
|
||||
"bulk_delete": {
|
||||
"label": "Preguntar abans d’esborrar diversos missatges",
|
||||
"description": "Això es configura a la pàgina Llista de missatges."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Sons i comptador de no llegits",
|
||||
"description": "Els sons de notificació i el nombre de no llegits a la pestanya es configuren a la pàgina Notificacions."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Quant a {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Tanca la sessió d’aquest compte en aquest dispositiu."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Configuració de Bulwark Webmail",
|
||||
"subtitle": "Configureu la vostra instància de correu web des del navegador.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {Es perdrà # missatge sense enviar.} other {Es perdran # missatges sense enviar.}}",
|
||||
"clear_confirm": "Buida",
|
||||
"persist_refused": "Aquest navegador pot esborrar el correu desat quan falti espai. Instal·lar l'aplicació sol evitar-ho.",
|
||||
"persist_retry": "Torna-ho a demanar",
|
||||
"persist_retry": "Demana al navegador que el conservi",
|
||||
"safari_hint": "El Safari esborra les dades d'un lloc després de 7 dies sense visitar-lo. Afegiu Bulwark a la pantalla d'inici o al Dock per mantenir el vostre correu en aquest dispositiu.",
|
||||
"evicted": "El navegador ha esborrat el correu desat en aquest dispositiu. Es torna a baixar.",
|
||||
"evicted_lost": "{count, plural, one {S'ha perdut # missatge o canvi sense enviar.} other {S'han perdut # missatges o canvis sense enviar.}}",
|
||||
|
||||
+80
-5
@@ -1985,7 +1985,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Automaticky vybírat adresu pro odpověď",
|
||||
"description": "Při odpovídání automaticky přepnout adresu odesílatele na identitu, která původně obdržela zprávu"
|
||||
"description": "Při odpovídání odeslat z adresy, na kterou byla zpráva poslána."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Porovnávání přijímací adresy",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Obnovit data z mezipaměti",
|
||||
"description": "Znovu načíst kontakty, kalendáře a složky ze serveru. Účty a přihlášení zůstanou zachovány – opraví zastaralé nebo chybné zobrazení bez odhlášení.",
|
||||
"description": "Znovu načíst kontakty, kalendáře a složky ze serveru. Zůstanete přihlášeni.",
|
||||
"button": "Obnovit",
|
||||
"confirm_title": "Obnovit data v mezipaměti?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Exportovat nastavení",
|
||||
"description": "Stáhnout svá nastavení jako soubor JSON",
|
||||
"description": "Stáhnout kopii nastavení",
|
||||
"button": "Exportovat"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Importovat nastavení",
|
||||
"description": "Nahrát nastavení ze souboru JSON",
|
||||
"description": "Obnovit nastavení ze souboru",
|
||||
"button": "Importovat"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Offline",
|
||||
"privacy": "Soukromí",
|
||||
"about": "O aplikaci"
|
||||
},
|
||||
"search": {
|
||||
"signature": "podpis e-mailový podpis zápatí",
|
||||
"sign_out": "odhlásit odhlášení logout",
|
||||
"app_lock": "PIN zámek kód uzamknout obrazovka",
|
||||
"text_size": "velikost písma větší text zoom zvětšit",
|
||||
"vacation": "automatická odpověď mimo kancelář nepřítomnost dovolená autoreply",
|
||||
"two_factor": "2FA dvoufázové dvoufaktorové MFA OTP autentikátor jednorázový kód",
|
||||
"encryption": "S/MIME smime PGP OpenPGP šifrování certifikát",
|
||||
"password": "heslo změnit heslo nové heslo",
|
||||
"offline": "offline bez připojení bez internetu stáhnout úložiště synchronizace",
|
||||
"wipe": "odebrat smazat vymazat zařízení data",
|
||||
"outbox": "k odeslání neodeslané čekající fronta",
|
||||
"remote_content": "vzdálené obrázky externí obsah sledování pixely důvěryhodní odesílatelé",
|
||||
"push": "push relay na pozadí oznámení",
|
||||
"version": "verze aktualizace o aplikaci zdrojový kód GitHub licence"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Oznámení",
|
||||
"push_link": {
|
||||
"label": "Relay pro oznámení",
|
||||
"description": "Oznámení při zavřené aplikaci jdou přes push relay, který nikdy nevidí vaši poštu. Vyberete ho na stránce Oznámení.",
|
||||
"button": "Oznámení"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Vzdálené obrázky a důvěryhodní odesílatelé",
|
||||
"receipts_label": "Potvrzení o přečtení",
|
||||
"senders_label": "Obrázky odesílatelů",
|
||||
"description": "Tato nastavení najdete na stránce Soukromí.",
|
||||
"button": "Soukromí"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Hustota a popisky panelu",
|
||||
"description": "Jak těsně jsou řádky seznamu a zda tlačítka ukazují svůj název, nastavíte na stránce Vzhled."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Světlé pozadí zpráv",
|
||||
"description": "Zobrazení zpráv na světlém pozadí v tmavém režimu nastavíte na stránce Čtení."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Náhled událostí v kalendáři",
|
||||
"description": "Zda najetí na událost ukáže její podrobnosti, nastavíte na stránce Kalendář."
|
||||
},
|
||||
"list": {
|
||||
"label": "Konverzace a řádky náhledu",
|
||||
"description": "Seskupení podle konverzace a řádek náhledu pod každou zprávou nastavíte na stránce Seznam zpráv."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Označení jako přečtené",
|
||||
"description": "Kdy se zpráva počítá jako přečtená a kam se dostanete po smazání nebo přesunu, nastavíte na stránce Čtení."
|
||||
},
|
||||
"send_warnings_description": "Vrácení odeslání a upozornění na chybějící předmět, zapomenutou přílohu nebo zástupný text.",
|
||||
"bulk_delete": {
|
||||
"label": "Dotaz před smazáním více zpráv",
|
||||
"description": "To nastavíte na stránce Seznam zpráv."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Zvuky a počet nepřečtených",
|
||||
"description": "Zvuky oznámení a počet nepřečtených na kartě nastavíte na stránce Oznámení."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "O aplikaci {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Odhlásit tento účet na tomto zařízení."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Nastavení Bulwark Webmail",
|
||||
"subtitle": "Nastavte svou instanci webmailu v prohlížeči.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# neodeslaná zpráva se ztratí.} few {# neodeslané zprávy se ztratí.} many {# neodeslané zprávy se ztratí.} other {# neodeslaných zpráv se ztratí.}}",
|
||||
"clear_confirm": "Vymazat",
|
||||
"persist_refused": "Tento prohlížeč může uchovanou poštu smazat, když dojde místo. Instalace aplikace tomu obvykle zabrání.",
|
||||
"persist_retry": "Zeptat se znovu",
|
||||
"persist_retry": "Požádat prohlížeč o zachování",
|
||||
"safari_hint": "Safari smaže data webu po 7 dnech bez návštěvy. Přidejte Bulwark na plochu nebo do Docku, aby vaše pošta na tomto zařízení zůstala.",
|
||||
"evicted": "Prohlížeč smazal poštu uchovanou na tomto zařízení. Stahuje se znovu.",
|
||||
"evicted_lost": "{count, plural, one {# neodeslaná zpráva nebo změna se ztratila.} few {# neodeslané zprávy nebo změny se ztratily.} many {# neodeslané zprávy nebo změny se ztratily.} other {# neodeslaných zpráv nebo změn se ztratilo.}}",
|
||||
|
||||
+80
-5
@@ -1988,7 +1988,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Svar fra modtaget adresse",
|
||||
"description": "Når du svarer, send fra den adresse som beskeden oprindeligt blev sendt til. Matcher først identiteter; for domæne catch-all-leveringer omskriver Fra-headeren til aliaset, mens der sendes gennem din primære identitet."
|
||||
"description": "Når du svarer, send fra den adresse, beskeden blev sendt til."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Matchning af modtageradresse",
|
||||
@@ -2632,7 +2632,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "Indlæs kontakter, kalendere og mapper fra serveren igen. Du forbliver logget ind.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "Opdater cachelagrede data?"
|
||||
},
|
||||
@@ -2645,12 +2645,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Eksportér indstillinger",
|
||||
"description": "Download dine indstillinger som JSON",
|
||||
"description": "Hent en kopi af dine indstillinger",
|
||||
"button": "Eksportér"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Importér indstillinger",
|
||||
"description": "Upload indstillinger fra JSON-fil",
|
||||
"description": "Gendan indstillinger fra en fil",
|
||||
"button": "Importér"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Offline",
|
||||
"privacy": "Privatliv",
|
||||
"about": "Om"
|
||||
},
|
||||
"search": {
|
||||
"signature": "signatur e-mailsignatur sidefod",
|
||||
"sign_out": "log ud logout afmeld",
|
||||
"app_lock": "PIN lås kode låseskærm",
|
||||
"text_size": "skriftstørrelse større tekst zoom forstør",
|
||||
"vacation": "autosvar automatisk svar ikke på kontoret fraværende ferie autoreply",
|
||||
"two_factor": "2FA totrins to-faktor MFA OTP godkendelsesapp engangskode",
|
||||
"encryption": "S/MIME smime PGP OpenPGP kryptering certifikat",
|
||||
"password": "adgangskode skift adgangskode ny adgangskode",
|
||||
"offline": "offline uden forbindelse uden internet hent lager synkroniser",
|
||||
"wipe": "fjern slet ryd enhed data",
|
||||
"outbox": "udbakke ikke sendt venter kø",
|
||||
"remote_content": "eksterne billeder eksternt indhold sporing pixels betroede afsendere",
|
||||
"push": "push relæ baggrund notifikationer",
|
||||
"version": "version opdatering om kildekode GitHub licens"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Notifikationer",
|
||||
"push_link": {
|
||||
"label": "Relæ til notifikationer",
|
||||
"description": "Notifikationer mens appen er lukket går gennem et push-relæ, som aldrig ser din mail. Du vælger det på siden Notifikationer.",
|
||||
"button": "Notifikationer"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Eksterne billeder og betroede afsendere",
|
||||
"receipts_label": "Læsebekræftelser",
|
||||
"senders_label": "Afsenderbilleder",
|
||||
"description": "Disse indstillinger findes på siden Privatliv.",
|
||||
"button": "Privatliv"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Tæthed og knapnavne",
|
||||
"description": "Hvor tæt listens rækker står, og om knapperne viser deres navn, indstilles på siden Udseende."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Lys baggrund til beskeder",
|
||||
"description": "At vise beskeder på lys baggrund i mørk tilstand indstilles på siden Læsning."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Forhåndsvisning af begivenheder i kalenderen",
|
||||
"description": "Om det viser detaljer at pege på en begivenhed, indstilles på siden Kalender."
|
||||
},
|
||||
"list": {
|
||||
"label": "Samtaler og eksempellinjer",
|
||||
"description": "Gruppering efter samtale og eksempellinjen under hver besked indstilles på siden Beskedliste."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Markér som læst",
|
||||
"description": "Hvornår en besked tæller som læst, og hvor du kommer hen efter at have slettet eller flyttet den, indstilles på siden Læsning."
|
||||
},
|
||||
"send_warnings_description": "Fortryd afsendelse og advarslerne om manglende emne, glemt vedhæftning eller pladsholdertekst.",
|
||||
"bulk_delete": {
|
||||
"label": "Spørg før sletning af flere beskeder",
|
||||
"description": "Det indstilles på siden Beskedliste."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Lyde og tæller for ulæste",
|
||||
"description": "Notifikationslyde og antallet af ulæste i fanen indstilles på siden Notifikationer."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Om {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Log ud af denne konto på denne enhed."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Opsætning af Bulwark Webmail",
|
||||
"subtitle": "Opsæt din webmailinstans i browseren.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# usendt besked går tabt.} other {# usendte beskeder går tabt.}}",
|
||||
"clear_confirm": "Ryd",
|
||||
"persist_refused": "Denne browser kan slette den beholdte mail, når pladsen bliver knap. Det hjælper normalt at installere appen.",
|
||||
"persist_retry": "Spørg igen",
|
||||
"persist_retry": "Bed browseren om at beholde den",
|
||||
"safari_hint": "Safari sletter et websteds data efter 7 dage uden besøg. Føj Bulwark til din hjemmeskærm eller til Dock for at beholde din mail på denne enhed.",
|
||||
"evicted": "Browseren slettede den mail, der var beholdt på denne enhed. Den hentes igen.",
|
||||
"evicted_lost": "{count, plural, one {# usendt besked eller ændring gik tabt.} other {# usendte beskeder eller ændringer gik tabt.}}",
|
||||
|
||||
+80
-5
@@ -1985,7 +1985,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Antwortadresse automatisch wählen",
|
||||
"description": "Beim Antworten die Absenderadresse automatisch auf die Identität umstellen, die die ursprüngliche Nachricht erhalten hat"
|
||||
"description": "Beim Antworten von der Adresse senden, an die die E-Mail ursprünglich ging."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Abgleich der Empfangsadresse",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Zwischengespeicherte Daten aktualisieren",
|
||||
"description": "Kontakte, Kalender und Ordner vom Server neu laden. Konten und Sitzungen bleiben erhalten; veraltete oder fehlerhafte Ansichten werden korrigiert, ohne dass Sie sich abmelden müssen.",
|
||||
"description": "Kontakte, Kalender und Ordner neu vom Server laden. Sie bleiben angemeldet.",
|
||||
"button": "Aktualisieren",
|
||||
"confirm_title": "Zwischengespeicherte Daten aktualisieren?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Einstellungen exportieren",
|
||||
"description": "Ihre Einstellungen als JSON herunterladen",
|
||||
"description": "Eine Kopie Ihrer Einstellungen herunterladen",
|
||||
"button": "Exportieren"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Einstellungen importieren",
|
||||
"description": "Einstellungen aus JSON-Datei hochladen",
|
||||
"description": "Einstellungen aus einer Datei wiederherstellen",
|
||||
"button": "Importieren"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Offline",
|
||||
"privacy": "Datenschutz",
|
||||
"about": "Über"
|
||||
},
|
||||
"search": {
|
||||
"signature": "Signatur E-Mail-Signatur Grußformel Fußzeile signature",
|
||||
"sign_out": "abmelden ausloggen Abmeldung logout sign out",
|
||||
"app_lock": "PIN Sperre Code Sperrbildschirm sperren",
|
||||
"text_size": "Schriftgröße größere Schrift Zoom vergrößern",
|
||||
"vacation": "Abwesenheit Abwesenheitsnotiz automatische Antwort Urlaub abwesend Autoreply out of office",
|
||||
"two_factor": "2FA Zwei-Faktor zweistufig MFA OTP Authenticator Einmalcode",
|
||||
"encryption": "S/MIME smime PGP OpenPGP Verschlüsselung Zertifikat",
|
||||
"password": "Passwort Kennwort ändern neues Passwort",
|
||||
"offline": "offline ohne Verbindung ohne Internet Mails herunterladen Speicher synchronisieren",
|
||||
"wipe": "entfernen löschen Gerät Daten zurücksetzen",
|
||||
"outbox": "Postausgang ungesendet wartend Warteschlange",
|
||||
"remote_content": "externe Bilder externe Inhalte Tracking Zählpixel vertrauenswürdige Absender",
|
||||
"push": "Push Relay Hintergrund Benachrichtigungen",
|
||||
"version": "Version Update Info Quellcode GitHub Lizenz"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Benachrichtigungen",
|
||||
"push_link": {
|
||||
"label": "Relay für Benachrichtigungen",
|
||||
"description": "Benachrichtigungen bei geschlossener App laufen über ein Push-Relay, das Ihre E-Mails nie sieht. Sie wählen es auf der Seite Benachrichtigungen.",
|
||||
"button": "Benachrichtigungen"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Externe Bilder und vertrauenswürdige Absender",
|
||||
"receipts_label": "Lesebestätigungen",
|
||||
"senders_label": "Absenderbilder",
|
||||
"description": "Diese Einstellungen finden Sie auf der Seite Datenschutz.",
|
||||
"button": "Datenschutz"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Dichte und Beschriftung der Symbolleiste",
|
||||
"description": "Wie eng die Zeilen der Liste stehen und ob Schaltflächen ihren Namen zeigen, stellen Sie auf der Seite Darstellung ein."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Heller Hintergrund für E-Mails",
|
||||
"description": "Ob E-Mails im dunklen Modus auf hellem Hintergrund erscheinen, stellen Sie auf der Seite Lesen ein."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Terminvorschau im Kalender",
|
||||
"description": "Ob beim Zeigen auf einen Termin seine Details erscheinen, stellen Sie auf der Seite Kalender ein."
|
||||
},
|
||||
"list": {
|
||||
"label": "Unterhaltungen und Vorschauzeilen",
|
||||
"description": "Die Gruppierung nach Unterhaltung und die Vorschauzeile unter jeder E-Mail stellen Sie auf der Seite Nachrichtenliste ein."
|
||||
},
|
||||
"reading": {
|
||||
"label": "E-Mails als gelesen markieren",
|
||||
"description": "Wann eine E-Mail als gelesen gilt und wohin Sie nach dem Löschen oder Verschieben kommen, stellen Sie auf der Seite Lesen ein."
|
||||
},
|
||||
"send_warnings_description": "Senden rückgängig machen und die Warnungen vor fehlendem Betreff, vergessenem Anhang oder Platzhaltertext.",
|
||||
"bulk_delete": {
|
||||
"label": "Nachfragen vor dem Löschen mehrerer E-Mails",
|
||||
"description": "Das stellen Sie auf der Seite Nachrichtenliste ein."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Töne und Zähler für Ungelesenes",
|
||||
"description": "Benachrichtigungstöne und die Zahl ungelesener E-Mails im Tab stellen Sie auf der Seite Benachrichtigungen ein."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Über {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Von diesem Konto auf diesem Gerät abmelden."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Einrichtung von Bulwark Webmail",
|
||||
"subtitle": "Richten Sie Ihre Webmail-Instanz im Browser ein.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# ungesendete Nachricht geht verloren.} other {# ungesendete Nachrichten gehen verloren.}}",
|
||||
"clear_confirm": "Leeren",
|
||||
"persist_refused": "Dieser Browser kann die behaltenen E-Mails löschen, wenn der Platz knapp wird. Die App zu installieren verhindert das meistens.",
|
||||
"persist_retry": "Erneut fragen",
|
||||
"persist_retry": "Browser bitten, sie zu behalten",
|
||||
"safari_hint": "Safari löscht die Daten einer Website nach 7 Tagen ohne Besuch. Fügen Sie Bulwark zum Home-Bildschirm oder zum Dock hinzu, damit Ihre E-Mails auf diesem Gerät bleiben.",
|
||||
"evicted": "Der Browser hat die auf diesem Gerät behaltenen E-Mails gelöscht. Sie werden neu geladen.",
|
||||
"evicted_lost": "{count, plural, one {# ungesendete Nachricht oder Änderung ging verloren.} other {# ungesendete Nachrichten oder Änderungen gingen verloren.}}",
|
||||
|
||||
@@ -310,7 +310,7 @@
|
||||
"description": "Alle E-Mails als reinen Text schreiben, ohne Fettdruck, Farben oder Bilder. Das gilt auch für Antworten und Weiterleitungen."
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"description": "Wenn Sie antworten, geht die Antwort von der Adresse aus, an die die E-Mail ging. Das klappt mit all Ihren eigenen Adressen. Weiter unten wählen Sie, ob auch andere Adressen Ihrer Domain zählen."
|
||||
"description": "Wenn Sie antworten, geht die Antwort von der Adresse aus, an die die E-Mail ging. Das klappt mit all Ihren eigenen Adressen."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Welche Adressen als Ihre gelten",
|
||||
@@ -574,10 +574,10 @@
|
||||
"description": "Alle Einstellungen auf den Anfangszustand zurücksetzen"
|
||||
},
|
||||
"export_settings": {
|
||||
"description": "Ihre Einstellungen als Datei auf Ihrem Gerät speichern"
|
||||
"description": "Eine Kopie Ihrer Einstellungen herunterladen"
|
||||
},
|
||||
"import_settings": {
|
||||
"description": "Ihre Einstellungen aus einer früher gespeicherten Datei laden"
|
||||
"description": "Einstellungen aus einer Datei wiederherstellen"
|
||||
},
|
||||
"sections": {
|
||||
"backup_description": "Dieselben Einstellungen in anderen Browsern und auf anderen Geräten nutzen.",
|
||||
|
||||
+80
-5
@@ -1988,7 +1988,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Reply from the address it was sent to",
|
||||
"description": "When replying, send from the address the message was originally sent to. Your configured identities always match; whether other addresses on your domains count too (catch-all) is set by “Received Address Matching” below."
|
||||
"description": "When replying, send from the address the message was originally sent to."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Received address matching",
|
||||
@@ -2636,7 +2636,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "Reload contacts, calendars and folders from the server. You stay signed in.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "Refresh cached data?"
|
||||
},
|
||||
@@ -2649,12 +2649,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Export settings",
|
||||
"description": "Download your settings as JSON",
|
||||
"description": "Download a copy of your settings",
|
||||
"button": "Export"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Import settings",
|
||||
"description": "Upload settings from JSON file",
|
||||
"description": "Restore settings from a file",
|
||||
"button": "Import"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Offline",
|
||||
"privacy": "Privacy",
|
||||
"about": "About"
|
||||
},
|
||||
"search": {
|
||||
"signature": "signature email signature sign-off footer",
|
||||
"sign_out": "sign out log out logout log off sign off",
|
||||
"app_lock": "PIN lock code passcode lock screen",
|
||||
"text_size": "font size bigger text larger text zoom",
|
||||
"vacation": "autoreply auto-reply automatic reply out of office away vacation holiday",
|
||||
"two_factor": "2FA two-factor two-step MFA OTP authenticator one-time code",
|
||||
"encryption": "S/MIME smime PGP OpenPGP encryption certificate",
|
||||
"password": "password change password new password",
|
||||
"offline": "offline without connection no internet download mail storage sync",
|
||||
"wipe": "remove erase wipe device data",
|
||||
"outbox": "outbox unsent waiting queued",
|
||||
"remote_content": "remote images external content tracking pixels trusted senders",
|
||||
"push": "push relay background notifications",
|
||||
"version": "version update about source code GitHub license"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Notifications",
|
||||
"push_link": {
|
||||
"label": "Relay for notifications",
|
||||
"description": "Notifications while the app is closed go through a push relay, which never sees your mail. You choose it on the Notifications page.",
|
||||
"button": "Notifications"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Remote images and trusted senders",
|
||||
"receipts_label": "Read receipts",
|
||||
"senders_label": "Sender pictures",
|
||||
"description": "These settings are on the Privacy page.",
|
||||
"button": "Privacy"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Density and toolbar labels",
|
||||
"description": "How close together list rows are, and whether toolbar buttons show their names, is set on the Appearance page."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Light background for messages",
|
||||
"description": "Showing messages on a light background in dark mode is set on the Reading page."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Event previews in the calendar",
|
||||
"description": "Whether pointing at an event shows its details is set on the Calendar page."
|
||||
},
|
||||
"list": {
|
||||
"label": "Conversations and preview lines",
|
||||
"description": "Grouping by conversation and the preview line under each message are set on the Message list page."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Marking mail as read",
|
||||
"description": "When a message counts as read, and where you go after deleting or moving it, is set on the Reading page."
|
||||
},
|
||||
"send_warnings_description": "Undo send, and the warnings about a missing subject, a forgotten attachment or placeholder text.",
|
||||
"bulk_delete": {
|
||||
"label": "Asking before deleting several messages",
|
||||
"description": "This is set on the Message list page."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Sounds and the unread badge",
|
||||
"description": "Notification sounds and the unread count on the tab are set on the Notifications page."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "About {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Sign out of this account on this device."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Bulwark Webmail setup",
|
||||
"subtitle": "Configure your webmail instance from the browser.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# message that was not sent is lost.} other {# messages that were not sent are lost.}}",
|
||||
"clear_confirm": "Clear",
|
||||
"persist_refused": "This browser may delete the kept mail when space runs low. Installing the app usually stops that.",
|
||||
"persist_retry": "Ask again",
|
||||
"persist_retry": "Ask the browser to keep it",
|
||||
"safari_hint": "Safari deletes a site's data after 7 days without a visit. Add Bulwark to your Home Screen or Dock to keep your mail on this device.",
|
||||
"evicted": "The browser deleted the mail kept on this device. It's downloading again.",
|
||||
"evicted_lost": "{count, plural, one {# unsent message or change was lost.} other {# unsent messages or changes were lost.}}",
|
||||
|
||||
@@ -312,7 +312,7 @@
|
||||
"description": "Write all messages as plain text, without bold, colours or pictures. This also applies to replies and forwards."
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"description": "When you reply, the reply is sent from the address the message was sent to. This works for all your own addresses. Below, you can choose if other addresses on your domain count too."
|
||||
"description": "When you reply, the reply is sent from the address the message was sent to. This works for all your own addresses."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Which addresses count as yours",
|
||||
@@ -577,10 +577,10 @@
|
||||
"description": "Put all settings back to how they were at the start"
|
||||
},
|
||||
"export_settings": {
|
||||
"description": "Save your settings as a file on your device"
|
||||
"description": "Download a copy of your settings"
|
||||
},
|
||||
"import_settings": {
|
||||
"description": "Load your settings from a file you saved before"
|
||||
"description": "Restore settings from a file"
|
||||
},
|
||||
"sections": {
|
||||
"backup_description": "Use the same settings in other browsers and on other devices.",
|
||||
|
||||
+80
-5
@@ -1976,7 +1976,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Seleccionar dirección de respuesta automáticamente",
|
||||
"description": "Al responder, cambia automáticamente la dirección del remitente a la identidad que recibió el mensaje original"
|
||||
"description": "Al responder, enviar desde la dirección a la que se envió el mensaje."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Coincidencia de la dirección de recepción",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "Volver a cargar contactos, calendarios y carpetas desde el servidor. Sigues con la sesión iniciada.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "¿Actualizar los datos en caché?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Exportar Configuración",
|
||||
"description": "Descargar su configuración como JSON",
|
||||
"description": "Descargar una copia de tus ajustes",
|
||||
"button": "Exportar"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Importar Configuración",
|
||||
"description": "Cargar configuración desde archivo JSON",
|
||||
"description": "Restaurar los ajustes desde un archivo",
|
||||
"button": "Importar"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Sin conexión",
|
||||
"privacy": "Privacidad",
|
||||
"about": "Acerca de"
|
||||
},
|
||||
"search": {
|
||||
"signature": "firma firma de correo pie de mensaje",
|
||||
"sign_out": "cerrar sesión salir desconectar logout",
|
||||
"app_lock": "PIN bloqueo código bloquear pantalla",
|
||||
"text_size": "tamaño de letra texto más grande zoom ampliar",
|
||||
"vacation": "respuesta automática fuera de la oficina ausente vacaciones autoreply",
|
||||
"two_factor": "2FA dos factores dos pasos MFA OTP autenticador código de un solo uso",
|
||||
"encryption": "S/MIME smime PGP OpenPGP cifrado certificado",
|
||||
"password": "contraseña cambiar contraseña nueva contraseña",
|
||||
"offline": "sin conexión offline sin internet descargar almacenamiento sincronizar",
|
||||
"wipe": "quitar borrar eliminar dispositivo datos",
|
||||
"outbox": "bandeja de salida sin enviar en espera cola",
|
||||
"remote_content": "imágenes remotas contenido externo rastreo píxeles remitentes de confianza",
|
||||
"push": "push relé segundo plano notificaciones",
|
||||
"version": "versión actualización acerca de código fuente GitHub licencia"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Notificaciones",
|
||||
"push_link": {
|
||||
"label": "Relé de notificaciones",
|
||||
"description": "Las notificaciones con la aplicación cerrada pasan por un relé push, que nunca ve tu correo. Lo eliges en la página Notificaciones.",
|
||||
"button": "Notificaciones"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Imágenes remotas y remitentes de confianza",
|
||||
"receipts_label": "Confirmaciones de lectura",
|
||||
"senders_label": "Imágenes de remitentes",
|
||||
"description": "Estos ajustes están en la página Privacidad.",
|
||||
"button": "Privacidad"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Densidad y etiquetas de la barra",
|
||||
"description": "La separación de las filas de la lista y si los botones muestran su nombre se ajustan en la página Apariencia."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Fondo claro para los mensajes",
|
||||
"description": "Mostrar los mensajes con fondo claro en modo oscuro se ajusta en la página Lectura."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Vista previa de eventos en el calendario",
|
||||
"description": "Si al señalar un evento se muestran sus detalles se ajusta en la página Calendario."
|
||||
},
|
||||
"list": {
|
||||
"label": "Conversaciones y líneas de vista previa",
|
||||
"description": "La agrupación por conversación y la línea de vista previa de cada mensaje se ajustan en la página Lista de mensajes."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Marcar como leído",
|
||||
"description": "Cuándo un mensaje cuenta como leído, y adónde vas tras borrarlo o moverlo, se ajusta en la página Lectura."
|
||||
},
|
||||
"send_warnings_description": "Deshacer envío y los avisos de asunto vacío, adjunto olvidado o texto de relleno.",
|
||||
"bulk_delete": {
|
||||
"label": "Preguntar antes de borrar varios mensajes",
|
||||
"description": "Esto se ajusta en la página Lista de mensajes."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Sonidos y contador de no leídos",
|
||||
"description": "Los sonidos de notificación y el número de no leídos en la pestaña se ajustan en la página Notificaciones."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Acerca de {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Cerrar sesión de esta cuenta en este dispositivo."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Configuración de Bulwark Webmail",
|
||||
"subtitle": "Configura tu instancia de webmail desde el navegador.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {Se perderá # mensaje sin enviar.} other {Se perderán # mensajes sin enviar.}}",
|
||||
"clear_confirm": "Vaciar",
|
||||
"persist_refused": "Este navegador puede borrar el correo guardado cuando falte espacio. Instalar la aplicación suele evitarlo.",
|
||||
"persist_retry": "Preguntar de nuevo",
|
||||
"persist_retry": "Pedir al navegador que lo conserve",
|
||||
"safari_hint": "Safari borra los datos de un sitio tras 7 días sin visitarlo. Añade Bulwark a tu pantalla de inicio o al Dock para conservar tu correo en este dispositivo.",
|
||||
"evicted": "El navegador borró el correo guardado en este dispositivo. Se está descargando de nuevo.",
|
||||
"evicted_lost": "{count, plural, one {Se perdió # mensaje o cambio sin enviar.} other {Se perdieron # mensajes o cambios sin enviar.}}",
|
||||
|
||||
+80
-5
@@ -1988,7 +1988,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Vasta kirja saanud aadressilt",
|
||||
"description": "Vastamisel saada kiri aadressilt, millele algne kiri saadeti. Sinu seadistatud identiteedid vastavad alati; kas ka teised sinu domeenide aadressid loetakse sobivaks (catch-all), määrab allpool olev „Vastuvõetud aadressi sobitamine“."
|
||||
"description": "Vastamisel saada aadressilt, kuhu kiri saadeti."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Vastuvõetud aadressi sobitamine",
|
||||
@@ -2636,7 +2636,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Värskenda vahemällu salvestatud andmeid",
|
||||
"description": "Laadi kontaktid, kalendrid ja kaustad serverist uuesti. Kontod ja seansid jäävad alles — parandab aegunud või vale kuva ilma välja logimata.",
|
||||
"description": "Laadi kontaktid, kalendrid ja kaustad serverist uuesti. Jääd sisselogituks.",
|
||||
"button": "Värskenda",
|
||||
"confirm_title": "Värskendada vahemälu andmed?"
|
||||
},
|
||||
@@ -2649,12 +2649,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Ekspordi seaded",
|
||||
"description": "Laadi oma seaded alla JSON-ina",
|
||||
"description": "Laadi alla oma seadete koopia",
|
||||
"button": "Ekspordi"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Impordi seaded",
|
||||
"description": "Lae seaded üles JSON-failist",
|
||||
"description": "Taasta seaded failist",
|
||||
"button": "Impordi"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Võrguühenduseta",
|
||||
"privacy": "Privaatsus",
|
||||
"about": "Teave"
|
||||
},
|
||||
"search": {
|
||||
"signature": "allkiri e-posti allkiri jalus",
|
||||
"sign_out": "logi välja väljalogimine logout",
|
||||
"app_lock": "PIN lukk kood lukustuskuva",
|
||||
"text_size": "fondi suurus suurem tekst suum suurendus",
|
||||
"vacation": "automaatvastus kontorist väljas eemal puhkus autoreply",
|
||||
"two_factor": "2FA kaheastmeline kahefaktoriline MFA OTP autentija ühekordne kood",
|
||||
"encryption": "S/MIME smime PGP OpenPGP krüpteerimine sertifikaat",
|
||||
"password": "parool muuda parooli uus parool",
|
||||
"offline": "võrguühenduseta offline ilma ühenduseta ilma internetita laadi alla salvestus sünkroonimine",
|
||||
"wipe": "eemalda kustuta tühjenda seade andmed",
|
||||
"outbox": "väljuvad saatmata ootel järjekord",
|
||||
"remote_content": "kaugpildid väline sisu jälgimine pikslid usaldusväärsed saatjad",
|
||||
"push": "push relee taustal teavitused",
|
||||
"version": "versioon uuendus teave lähtekood GitHub litsents"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Teavitused",
|
||||
"push_link": {
|
||||
"label": "Teavituste relee",
|
||||
"description": "Teavitused suletud rakenduse ajal käivad läbi push-relee, mis ei näe kunagi sinu kirju. Valid selle lehel Teavitused.",
|
||||
"button": "Teavitused"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Kaugpildid ja usaldusväärsed saatjad",
|
||||
"receipts_label": "Lugemiskinnitused",
|
||||
"senders_label": "Saatjate pildid",
|
||||
"description": "Need seaded on lehel Privaatsus.",
|
||||
"button": "Privaatsus"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Tihedus ja nuppude nimed",
|
||||
"description": "Kui tihedalt loendi read on ja kas nupud näitavad oma nime, seadistad lehel Välimus."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Hele taust kirjadele",
|
||||
"description": "Kirjade näitamise heledal taustal tumedas režiimis seadistad lehel Lugemine."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Sündmuste eelvaade kalendris",
|
||||
"description": "Kas sündmusele osutamine näitab üksikasju, seadistad lehel Kalender."
|
||||
},
|
||||
"list": {
|
||||
"label": "Vestlused ja eelvaate read",
|
||||
"description": "Vestluste kaupa rühmitamise ja iga kirja all oleva eelvaate rea seadistad lehel Kirjade loend."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Loetuks märkimine",
|
||||
"description": "Millal kiri loetakse loetuks ja kuhu jõuad pärast kustutamist või teisaldamist, seadistad lehel Lugemine."
|
||||
},
|
||||
"send_warnings_description": "Saatmise tagasivõtmine ja hoiatused puuduva teema, unustatud manuse või kohatäiteteksti kohta.",
|
||||
"bulk_delete": {
|
||||
"label": "Küsi enne mitme kirja kustutamist",
|
||||
"description": "Selle seadistad lehel Kirjade loend."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Helid ja lugemata kirjade arv",
|
||||
"description": "Teavitushelid ja lugemata kirjade arvu vahekaardil seadistad lehel Teavitused."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Teave: {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Logi sellest kontost selles seadmes välja."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Bulwark Webmaili seadistamine",
|
||||
"subtitle": "Seadista oma veebipostiteenus brauseris.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# saatmata kiri läheb kaotsi.} other {# saatmata kirja lähevad kaotsi.}}",
|
||||
"clear_confirm": "Tühjenda",
|
||||
"persist_refused": "See brauser võib hoitud kirjad kustutada, kui ruumi napib. Rakenduse paigaldamine hoiab selle tavaliselt ära.",
|
||||
"persist_retry": "Küsi uuesti",
|
||||
"persist_retry": "Palu brauseril need alles hoida",
|
||||
"safari_hint": "Safari kustutab saidi andmed, kui seda pole 7 päeva külastatud. Lisa Bulwark avaekraanile või Dockile, et su kirjad sellesse seadmesse alles jääksid.",
|
||||
"evicted": "Brauser kustutas selles seadmes hoitud kirjad. Need laaditakse uuesti alla.",
|
||||
"evicted_lost": "{count, plural, one {# saatmata kiri või muudatus läks kaotsi.} other {# saatmata kirja või muudatust läks kaotsi.}}",
|
||||
|
||||
+80
-5
@@ -1988,7 +1988,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "پاسخ از آدرس دریافتی",
|
||||
"description": "ارسال با آدرس دریافت شده"
|
||||
"description": "هنگام پاسخ، از نشانیای که پیام به آن فرستاده شده ارسال شود."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "تطبیق نشانی دریافت",
|
||||
@@ -2636,7 +2636,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "بارگیری دوباره مخاطبان، تقویمها و پوشهها از سرور. وارد حساب میمانید.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "دادههای ذخیرهشده موقت تازه شوند؟"
|
||||
},
|
||||
@@ -2649,12 +2649,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "خروجی تنظیمات",
|
||||
"description": "دانلود تنظیمات به صورت JSON",
|
||||
"description": "دانلود یک نسخه از تنظیمات",
|
||||
"button": "خروجی"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "وارد کردن تنظیمات",
|
||||
"description": "بارگذاری تنظیمات از فایل JSON",
|
||||
"description": "بازیابی تنظیمات از یک فایل",
|
||||
"button": "وارد کردن"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "آفلاین",
|
||||
"privacy": "حریم خصوصی",
|
||||
"about": "درباره"
|
||||
},
|
||||
"search": {
|
||||
"signature": "امضا امضای ایمیل پانویس",
|
||||
"sign_out": "خروج خروج از حساب logout",
|
||||
"app_lock": "PIN قفل کد صفحه قفل",
|
||||
"text_size": "اندازه قلم متن بزرگتر بزرگنمایی زوم",
|
||||
"vacation": "پاسخ خودکار خارج از دفتر غایب مرخصی تعطیلات autoreply",
|
||||
"two_factor": "2FA دومرحلهای دوعاملی MFA OTP احراز هویت کد یکبارمصرف",
|
||||
"encryption": "S/MIME smime PGP OpenPGP رمزگذاری گواهی",
|
||||
"password": "گذرواژه رمز عبور تغییر گذرواژه گذرواژه جدید",
|
||||
"offline": "آفلاین offline بدون اتصال بدون اینترنت دانلود فضای ذخیره همگامسازی",
|
||||
"wipe": "حذف پاک کردن دستگاه دادهها",
|
||||
"outbox": "صندوق خروجی ارسالنشده در انتظار صف",
|
||||
"remote_content": "تصاویر راه دور محتوای خارجی ردیابی پیکسل فرستندگان مورد اعتماد",
|
||||
"push": "push رله پسزمینه اعلانها",
|
||||
"version": "نسخه بهروزرسانی درباره کد منبع GitHub مجوز"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "اعلانها",
|
||||
"push_link": {
|
||||
"label": "رله اعلانها",
|
||||
"description": "اعلانها وقتی برنامه بسته است از یک رله پوش میگذرند که هرگز ایمیل شما را نمیبیند. آن را در صفحه اعلانها انتخاب میکنید.",
|
||||
"button": "اعلانها"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "تصاویر راه دور و فرستندگان مورد اعتماد",
|
||||
"receipts_label": "رسید خواندن",
|
||||
"senders_label": "تصاویر فرستندگان",
|
||||
"description": "این تنظیمات در صفحه حریم خصوصی هستند.",
|
||||
"button": "حریم خصوصی"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "تراکم و برچسب دکمهها",
|
||||
"description": "فاصله ردیفهای فهرست و نمایش نام دکمهها در صفحه ظاهر تنظیم میشود."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "پسزمینه روشن برای پیامها",
|
||||
"description": "نمایش پیامها روی پسزمینه روشن در حالت تیره در صفحه خواندن تنظیم میشود."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "پیشنمایش رویدادها در تقویم",
|
||||
"description": "نمایش جزئیات با اشاره به یک رویداد در صفحه تقویم تنظیم میشود."
|
||||
},
|
||||
"list": {
|
||||
"label": "گفتگوها و خطوط پیشنمایش",
|
||||
"description": "گروهبندی بر اساس گفتگو و خط پیشنمایش زیر هر پیام در صفحه فهرست پیامها تنظیم میشود."
|
||||
},
|
||||
"reading": {
|
||||
"label": "علامتگذاری بهعنوان خواندهشده",
|
||||
"description": "اینکه پیام کی خواندهشده حساب شود و پس از حذف یا انتقال به کجا بروید، در صفحه خواندن تنظیم میشود."
|
||||
},
|
||||
"send_warnings_description": "لغو ارسال و هشدارها درباره موضوع خالی، پیوست فراموششده یا متن جایگزین.",
|
||||
"bulk_delete": {
|
||||
"label": "پرسیدن پیش از حذف چند پیام",
|
||||
"description": "این در صفحه فهرست پیامها تنظیم میشود."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "صداها و شمارنده خواندهنشده",
|
||||
"description": "صداهای اعلان و تعداد خواندهنشدهها روی زبانه در صفحه اعلانها تنظیم میشود."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "درباره {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "خروج از این حساب در این دستگاه."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "راهاندازی Bulwark Webmail",
|
||||
"subtitle": "نمونهٔ وبمیل خود را از مرورگر پیکربندی کنید.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# پیام ارسالنشده از بین میرود.} other {# پیام ارسالنشده از بین میروند.}}",
|
||||
"clear_confirm": "پاککردن",
|
||||
"persist_refused": "وقتی فضا کم شود، این مرورگر ممکن است ایمیلهای نگهداشتهشده را حذف کند. نصب برنامه معمولاً جلوی این را میگیرد.",
|
||||
"persist_retry": "دوباره بپرس",
|
||||
"persist_retry": "از مرورگر بخواهید آن را نگه دارد",
|
||||
"safari_hint": "Safari دادههای یک سایت را پس از ۷ روز بدون بازدید حذف میکند. Bulwark را به صفحه اصلی یا Dock اضافه کنید تا ایمیلهایتان روی این دستگاه بماند.",
|
||||
"evicted": "مرورگر ایمیلهای نگهداشتهشده روی این دستگاه را حذف کرد. دوباره دانلود میشوند.",
|
||||
"evicted_lost": "{count, plural, one {# پیام یا تغییر ارسالنشده از بین رفت.} other {# پیام یا تغییر ارسالنشده از بین رفتند.}}",
|
||||
|
||||
+80
-5
@@ -1976,7 +1976,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Sélection automatique de l'adresse de réponse",
|
||||
"description": "Lors d'une réponse, bascule automatiquement l'adresse d'expédition vers l'identité qui a reçu le message d'origine"
|
||||
"description": "Lors d’une réponse, envoyer depuis l’adresse à laquelle le message a été envoyé."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Correspondance de l'adresse de réception",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Actualiser les données en cache",
|
||||
"description": "Actualiser les contacts, calendriers et dossiers depuis le serveur. Vos comptes et sessions sont conservés. Corrige un affichage obsolète ou incorrect sans vous déconnecter.",
|
||||
"description": "Recharger les contacts, calendriers et dossiers depuis le serveur. Vous restez connecté.",
|
||||
"button": "Actualiser",
|
||||
"confirm_title": "Actualiser les données en cache ?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Exporter les paramètres",
|
||||
"description": "Télécharger vos paramètres au format JSON",
|
||||
"description": "Télécharger une copie de vos réglages",
|
||||
"button": "Exporter"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Importer les paramètres",
|
||||
"description": "Charger les paramètres depuis un fichier JSON",
|
||||
"description": "Restaurer les réglages depuis un fichier",
|
||||
"button": "Importer"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Hors ligne",
|
||||
"privacy": "Confidentialité",
|
||||
"about": "À propos"
|
||||
},
|
||||
"search": {
|
||||
"signature": "signature signature e-mail pied de message",
|
||||
"sign_out": "déconnexion se déconnecter quitter logout",
|
||||
"app_lock": "PIN verrou code verrouillage écran",
|
||||
"text_size": "taille de police texte plus grand zoom agrandir",
|
||||
"vacation": "réponse automatique absence congés vacances absent autoreply",
|
||||
"two_factor": "2FA double authentification deux facteurs MFA OTP authentificateur code unique",
|
||||
"encryption": "S/MIME smime PGP OpenPGP chiffrement certificat",
|
||||
"password": "mot de passe changer nouveau mot de passe",
|
||||
"offline": "hors ligne sans connexion sans internet télécharger stockage synchronisation",
|
||||
"wipe": "supprimer effacer appareil données",
|
||||
"outbox": "boîte d’envoi non envoyé en attente file",
|
||||
"remote_content": "images distantes contenu externe pistage pixels expéditeurs de confiance",
|
||||
"push": "push relais arrière-plan notifications",
|
||||
"version": "version mise à jour à propos code source GitHub licence"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Notifications",
|
||||
"push_link": {
|
||||
"label": "Relais des notifications",
|
||||
"description": "Les notifications reçues quand l’application est fermée passent par un relais push, qui ne voit jamais vos e-mails. Vous le choisissez sur la page Notifications.",
|
||||
"button": "Notifications"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Images distantes et expéditeurs de confiance",
|
||||
"receipts_label": "Accusés de lecture",
|
||||
"senders_label": "Images des expéditeurs",
|
||||
"description": "Ces réglages se trouvent sur la page Confidentialité.",
|
||||
"button": "Confidentialité"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Densité et libellés de la barre d’outils",
|
||||
"description": "L’espacement des lignes de la liste et l’affichage du nom des boutons se règlent sur la page Apparence."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Fond clair pour les messages",
|
||||
"description": "L’affichage des messages sur fond clair en mode sombre se règle sur la page Lecture."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Aperçu des événements dans le calendrier",
|
||||
"description": "L’affichage des détails en pointant un événement se règle sur la page Calendrier."
|
||||
},
|
||||
"list": {
|
||||
"label": "Conversations et lignes d’aperçu",
|
||||
"description": "Le regroupement par conversation et la ligne d’aperçu sous chaque message se règlent sur la page Liste des messages."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Marquer comme lu",
|
||||
"description": "Le moment où un message compte comme lu, et la page affichée après une suppression ou un déplacement, se règlent sur la page Lecture."
|
||||
},
|
||||
"send_warnings_description": "Annulation de l’envoi, et les avertissements pour un objet manquant, une pièce jointe oubliée ou un texte provisoire.",
|
||||
"bulk_delete": {
|
||||
"label": "Confirmer avant de supprimer plusieurs messages",
|
||||
"description": "Ce réglage se trouve sur la page Liste des messages."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Sons et badge des non lus",
|
||||
"description": "Les sons de notification et le nombre de non lus dans l’onglet se règlent sur la page Notifications."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "À propos de {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Se déconnecter de ce compte sur cet appareil."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Configuration de Bulwark Webmail",
|
||||
"subtitle": "Configurez votre instance de webmail depuis le navigateur.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# message non envoyé sera perdu.} other {# messages non envoyés seront perdus.}}",
|
||||
"clear_confirm": "Vider",
|
||||
"persist_refused": "Ce navigateur peut supprimer les e-mails gardés quand la place manque. Installer l'application l'évite en général.",
|
||||
"persist_retry": "Redemander",
|
||||
"persist_retry": "Demander au navigateur de les garder",
|
||||
"safari_hint": "Safari supprime les données d'un site après 7 jours sans visite. Ajoutez Bulwark à votre écran d'accueil ou au Dock pour garder vos e-mails sur cet appareil.",
|
||||
"evicted": "Le navigateur a supprimé les e-mails gardés sur cet appareil. Ils sont de nouveau en cours de téléchargement.",
|
||||
"evicted_lost": "{count, plural, one {# message ou modification non envoyé a été perdu.} other {# messages ou modifications non envoyés ont été perdus.}}",
|
||||
|
||||
+80
-5
@@ -1917,7 +1917,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "בחירה אוטומטית בכתובת תשובה",
|
||||
"description": "בעת תשובה, העבר אוטומטית את הכתובת מאת לזהות שקיבלה את ההודעה במקור"
|
||||
"description": "בעת תשובה, לשלוח מהכתובת שאליה נשלחה ההודעה."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "התאמת כתובת הקבלה",
|
||||
@@ -2569,7 +2569,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "רענון נתונים שמורים",
|
||||
"description": "טעינה מחדש של אנשי קשר, לוחות שנה ותיקיות מהשרת. שומר על החשבונות וההתחברויות שלך — מתקן תצוגה מיושנת או שגויה ללא התנתקות.",
|
||||
"description": "טעינה מחדש של אנשי קשר, לוחות שנה ותיקיות מהשרת. נשארים מחוברים.",
|
||||
"button": "רענון",
|
||||
"confirm_title": "לרענן את הנתונים השמורים במטמון?"
|
||||
},
|
||||
@@ -2582,12 +2582,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "ייצוא הגדרות",
|
||||
"description": "הורד את ההגדרות שלך בתור JSON",
|
||||
"description": "הורדת עותק של ההגדרות",
|
||||
"button": "ייצוא"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "הגדרות ייבוא",
|
||||
"description": "העלה הגדרות מקובץ JSON",
|
||||
"description": "שחזור הגדרות מקובץ",
|
||||
"button": "ייבוא"
|
||||
},
|
||||
"about": {
|
||||
@@ -3332,6 +3332,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "לא מקוון",
|
||||
"privacy": "פרטיות",
|
||||
"about": "אודות"
|
||||
},
|
||||
"search": {
|
||||
"signature": "חתימה חתימת דואר כותרת תחתונה",
|
||||
"sign_out": "התנתקות יציאה logout",
|
||||
"app_lock": "PIN נעילה קוד מסך נעילה",
|
||||
"text_size": "גודל גופן טקסט גדול יותר זום הגדלה",
|
||||
"vacation": "מענה אוטומטי מחוץ למשרד לא זמין חופשה autoreply",
|
||||
"two_factor": "2FA דו־שלבי דו־גורמי MFA OTP מאמת קוד חד־פעמי",
|
||||
"encryption": "S/MIME smime PGP OpenPGP הצפנה תעודה",
|
||||
"password": "סיסמה שינוי סיסמה סיסמה חדשה",
|
||||
"offline": "לא מקוון offline ללא חיבור ללא אינטרנט הורדה אחסון סנכרון",
|
||||
"wipe": "הסרה מחיקה ניקוי מכשיר נתונים",
|
||||
"outbox": "דואר יוצא לא נשלח ממתין תור",
|
||||
"remote_content": "תמונות מרוחקות תוכן חיצוני מעקב פיקסלים שולחים מהימנים",
|
||||
"push": "push ממסר רקע התראות",
|
||||
"version": "גרסה עדכון אודות קוד מקור GitHub רישיון"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "התראות",
|
||||
"push_link": {
|
||||
"label": "ממסר להתראות",
|
||||
"description": "התראות כשהאפליקציה סגורה עוברות דרך ממסר push שלעולם אינו רואה את הדואר שלך. בוחרים אותו בדף התראות.",
|
||||
"button": "התראות"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "תמונות מרוחקות ושולחים מהימנים",
|
||||
"receipts_label": "אישורי קריאה",
|
||||
"senders_label": "תמונות השולחים",
|
||||
"description": "ההגדרות האלה נמצאות בדף פרטיות.",
|
||||
"button": "פרטיות"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "צפיפות ותוויות כפתורים",
|
||||
"description": "עד כמה צפופות שורות הרשימה והאם הכפתורים מציגים את שמם נקבע בדף מראה."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "רקע בהיר להודעות",
|
||||
"description": "הצגת הודעות על רקע בהיר במצב כהה נקבעת בדף קריאה."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "תצוגה מקדימה של אירועים בלוח השנה",
|
||||
"description": "האם הצבעה על אירוע מציגה את פרטיו נקבע בדף לוח שנה."
|
||||
},
|
||||
"list": {
|
||||
"label": "שיחות ושורות תצוגה מקדימה",
|
||||
"description": "קיבוץ לפי שיחה ושורת התצוגה המקדימה מתחת לכל הודעה נקבעים בדף רשימת הודעות."
|
||||
},
|
||||
"reading": {
|
||||
"label": "סימון כנקרא",
|
||||
"description": "מתי הודעה נחשבת לנקראה ולאן עוברים אחרי מחיקה או העברה נקבע בדף קריאה."
|
||||
},
|
||||
"send_warnings_description": "ביטול שליחה והאזהרות על נושא חסר, קובץ מצורף שנשכח או טקסט ממלא מקום.",
|
||||
"bulk_delete": {
|
||||
"label": "לשאול לפני מחיקת כמה הודעות",
|
||||
"description": "זה נקבע בדף רשימת הודעות."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "צלילים ומונה הלא נקראו",
|
||||
"description": "צלילי ההתראות ומספר הלא נקראו בכרטיסייה נקבעים בדף התראות."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "אודות {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "התנתקות מהחשבון הזה במכשיר הזה."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "הגדרת Bulwark Webmail",
|
||||
"subtitle": "הגדירו את מופע דואר הרשת שלכם מהדפדפן.",
|
||||
@@ -5895,7 +5970,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {הודעה אחת שלא נשלחה תאבד.} two {# הודעות שלא נשלחו יאבדו.} other {# הודעות שלא נשלחו יאבדו.}}",
|
||||
"clear_confirm": "ניקוי",
|
||||
"persist_refused": "הדפדפן הזה עלול למחוק את הדואר השמור כשנגמר המקום. התקנת האפליקציה בדרך כלל מונעת זאת.",
|
||||
"persist_retry": "לשאול שוב",
|
||||
"persist_retry": "לבקש מהדפדפן לשמור עליהם",
|
||||
"safari_hint": "Safari מוחק את נתוני האתר אחרי 7 ימים בלי ביקור. הוסיפו את Bulwark למסך הבית או ל-Dock כדי לשמור את הדואר במכשיר הזה.",
|
||||
"evicted": "הדפדפן מחק את הדואר ששמור במכשיר הזה. הוא יורד מחדש.",
|
||||
"evicted_lost": "{count, plural, one {הודעה אחת או שינוי אחד שלא נשלחו אבדו.} two {# הודעות או שינויים שלא נשלחו אבדו.} other {# הודעות או שינויים שלא נשלחו אבדו.}}",
|
||||
|
||||
+80
-5
@@ -1988,7 +1988,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Válasz a fogadott címről",
|
||||
"description": "Válaszküldéskor arról a címről küldjön, amelyre az üzenet eredetileg érkezett. Először identitásokat keres; tartományi catch-all kézbesítések esetén átírja a Feladó fejlécet az aliasra, miközben az elsődleges identitáson keresztül küld."
|
||||
"description": "Válaszkor arról a címről küldjön, amelyre az üzenet érkezett."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Fogadó cím egyeztetése",
|
||||
@@ -2632,7 +2632,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "Névjegyek, naptárak és mappák újratöltése a szerverről. Bejelentkezve marad.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "Frissíti a gyorsítótárazott adatokat?"
|
||||
},
|
||||
@@ -2645,12 +2645,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Beállítások exportálása",
|
||||
"description": "Beállítások letöltése JSON-ként",
|
||||
"description": "Másolat letöltése a beállításokról",
|
||||
"button": "Exportálás"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Beállítások importálása",
|
||||
"description": "Beállítások feltöltése JSON fájlból",
|
||||
"description": "Beállítások visszaállítása fájlból",
|
||||
"button": "Importálás"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Offline",
|
||||
"privacy": "Adatvédelem",
|
||||
"about": "Névjegy"
|
||||
},
|
||||
"search": {
|
||||
"signature": "aláírás e-mail aláírás lábléc",
|
||||
"sign_out": "kijelentkezés kilépés logout",
|
||||
"app_lock": "PIN zár kód zárolás képernyő",
|
||||
"text_size": "betűméret nagyobb szöveg nagyítás zoom",
|
||||
"vacation": "automatikus válasz házon kívül távol szabadság autoreply",
|
||||
"two_factor": "2FA kétlépcsős kéttényezős MFA OTP hitelesítő egyszeri kód",
|
||||
"encryption": "S/MIME smime PGP OpenPGP titkosítás tanúsítvány",
|
||||
"password": "jelszó jelszócsere új jelszó",
|
||||
"offline": "offline kapcsolat nélkül internet nélkül letöltés tárhely szinkronizálás",
|
||||
"wipe": "eltávolítás törlés kiürítés eszköz adatok",
|
||||
"outbox": "kimenő elküldetlen várakozó sor",
|
||||
"remote_content": "távoli képek külső tartalom követés pixelek megbízható feladók",
|
||||
"push": "push továbbító háttér értesítések",
|
||||
"version": "verzió frissítés névjegy forráskód GitHub licenc"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Értesítések",
|
||||
"push_link": {
|
||||
"label": "Értesítési továbbító",
|
||||
"description": "A bezárt alkalmazás értesítései egy push továbbítón mennek át, amely soha nem látja a leveleit. Az Értesítések oldalon választhatja ki.",
|
||||
"button": "Értesítések"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Távoli képek és megbízható feladók",
|
||||
"receipts_label": "Olvasási visszaigazolások",
|
||||
"senders_label": "Feladók képei",
|
||||
"description": "Ezek a beállítások az Adatvédelem oldalon vannak.",
|
||||
"button": "Adatvédelem"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Sűrűség és gombfeliratok",
|
||||
"description": "A lista sorainak sűrűségét és azt, hogy a gombok mutatják-e a nevüket, a Megjelenés oldalon állíthatja be."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Világos háttér az üzeneteknek",
|
||||
"description": "Az üzenetek világos háttéren való megjelenítését sötét módban az Olvasás oldalon állíthatja be."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Események előnézete a naptárban",
|
||||
"description": "Azt, hogy egy eseményre mutatva megjelennek-e a részletei, a Naptár oldalon állíthatja be."
|
||||
},
|
||||
"list": {
|
||||
"label": "Beszélgetések és előnézeti sorok",
|
||||
"description": "A beszélgetések szerinti csoportosítást és az üzenetek alatti előnézeti sort az Üzenetlista oldalon állíthatja be."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Olvasottnak jelölés",
|
||||
"description": "Azt, hogy mikor számít olvasottnak egy üzenet, és hová kerül törlés vagy áthelyezés után, az Olvasás oldalon állíthatja be."
|
||||
},
|
||||
"send_warnings_description": "Küldés visszavonása és a figyelmeztetések hiányzó tárgy, elfelejtett melléklet vagy helykitöltő szöveg esetén.",
|
||||
"bulk_delete": {
|
||||
"label": "Kérdés több üzenet törlése előtt",
|
||||
"description": "Ezt az Üzenetlista oldalon állíthatja be."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Hangok és az olvasatlanok száma",
|
||||
"description": "Az értesítési hangokat és a lapon látható olvasatlan számot az Értesítések oldalon állíthatja be."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Névjegy: {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Kijelentkezés ebből a fiókból ezen az eszközön."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "A Bulwark Webmail beállítása",
|
||||
"subtitle": "Állítsa be a webmail-példányát a böngészőből.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# el nem küldött levél elvész.} other {# el nem küldött levél elvész.}}",
|
||||
"clear_confirm": "Kiürítés",
|
||||
"persist_refused": "Ez a böngésző törölheti a megtartott leveleket, ha fogy a hely. Az alkalmazás telepítése ezt általában megakadályozza.",
|
||||
"persist_retry": "Újra rákérdez",
|
||||
"persist_retry": "A böngésző megkérése a megőrzésre",
|
||||
"safari_hint": "A Safari 7 nap látogatás nélkül törli egy webhely adatait. Adja hozzá a Bulwarkot a Főképernyőhöz vagy a Dockhoz, hogy a levelei az eszközön maradjanak.",
|
||||
"evicted": "A böngésző törölte az ezen az eszközön megtartott leveleket. Újra letöltődnek.",
|
||||
"evicted_lost": "{count, plural, one {# el nem küldött levél vagy módosítás elveszett.} other {# el nem küldött levél vagy módosítás elveszett.}}",
|
||||
|
||||
+80
-5
@@ -1976,7 +1976,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Seleziona automaticamente l'indirizzo di risposta",
|
||||
"description": "Quando rispondi, passa automaticamente l'indirizzo mittente all'identità che ha ricevuto il messaggio originale"
|
||||
"description": "Quando rispondi, invia dall’indirizzo a cui è stato inviato il messaggio."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Corrispondenza dell'indirizzo di ricezione",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "Ricarica contatti, calendari e cartelle dal server. Resti connesso.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "Aggiornare i dati nella cache?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Esporta impostazioni",
|
||||
"description": "Scarica le tue impostazioni come JSON",
|
||||
"description": "Scarica una copia delle impostazioni",
|
||||
"button": "Esporta"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Importa impostazioni",
|
||||
"description": "Carica impostazioni da file JSON",
|
||||
"description": "Ripristina le impostazioni da un file",
|
||||
"button": "Importa"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Offline",
|
||||
"privacy": "Privacy",
|
||||
"about": "Informazioni"
|
||||
},
|
||||
"search": {
|
||||
"signature": "firma firma email piè di pagina",
|
||||
"sign_out": "esci disconnetti logout uscita",
|
||||
"app_lock": "PIN blocco codice blocca schermo",
|
||||
"text_size": "dimensione carattere testo più grande zoom ingrandire",
|
||||
"vacation": "risposta automatica fuori sede assente ferie vacanza autoreply",
|
||||
"two_factor": "2FA due fattori due passaggi MFA OTP autenticatore codice monouso",
|
||||
"encryption": "S/MIME smime PGP OpenPGP crittografia certificato",
|
||||
"password": "password cambia password nuova password",
|
||||
"offline": "offline senza connessione senza internet scaricare archiviazione sincronizzare",
|
||||
"wipe": "rimuovi cancella elimina dispositivo dati",
|
||||
"outbox": "posta in uscita non inviati in attesa coda",
|
||||
"remote_content": "immagini remote contenuti esterni tracciamento pixel mittenti attendibili",
|
||||
"push": "push relay in background notifiche",
|
||||
"version": "versione aggiornamento informazioni codice sorgente GitHub licenza"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Notifiche",
|
||||
"push_link": {
|
||||
"label": "Relay per le notifiche",
|
||||
"description": "Le notifiche ad app chiusa passano da un relay push, che non vede mai la tua posta. Lo scegli nella pagina Notifiche.",
|
||||
"button": "Notifiche"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Immagini remote e mittenti attendibili",
|
||||
"receipts_label": "Conferme di lettura",
|
||||
"senders_label": "Immagini dei mittenti",
|
||||
"description": "Queste impostazioni sono nella pagina Privacy.",
|
||||
"button": "Privacy"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Densità ed etichette della barra",
|
||||
"description": "La distanza tra le righe dell’elenco e la presenza del nome sui pulsanti si impostano nella pagina Aspetto."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Sfondo chiaro per i messaggi",
|
||||
"description": "Mostrare i messaggi su sfondo chiaro in modalità scura si imposta nella pagina Lettura."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Anteprima degli eventi nel calendario",
|
||||
"description": "Se puntando un evento compaiono i dettagli si imposta nella pagina Calendario."
|
||||
},
|
||||
"list": {
|
||||
"label": "Conversazioni e righe di anteprima",
|
||||
"description": "Il raggruppamento per conversazione e la riga di anteprima sotto ogni messaggio si impostano nella pagina Elenco messaggi."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Segnare come letto",
|
||||
"description": "Quando un messaggio conta come letto, e dove vai dopo averlo eliminato o spostato, si imposta nella pagina Lettura."
|
||||
},
|
||||
"send_warnings_description": "Annulla invio e gli avvisi per oggetto mancante, allegato dimenticato o testo segnaposto.",
|
||||
"bulk_delete": {
|
||||
"label": "Chiedere prima di eliminare più messaggi",
|
||||
"description": "Si imposta nella pagina Elenco messaggi."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Suoni e contatore dei non letti",
|
||||
"description": "I suoni delle notifiche e il numero di non letti nella scheda si impostano nella pagina Notifiche."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Informazioni su {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Esci da questo account su questo dispositivo."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Configurazione di Bulwark Webmail",
|
||||
"subtitle": "Configura la tua istanza webmail dal browser.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# messaggio non inviato andrà perso.} other {# messaggi non inviati andranno persi.}}",
|
||||
"clear_confirm": "Svuota",
|
||||
"persist_refused": "Questo browser può eliminare la posta conservata quando lo spazio scarseggia. Installare l'app di solito lo evita.",
|
||||
"persist_retry": "Chiedi di nuovo",
|
||||
"persist_retry": "Chiedi al browser di conservarla",
|
||||
"safari_hint": "Safari elimina i dati di un sito dopo 7 giorni senza visite. Aggiungi Bulwark alla schermata Home o al Dock per conservare la tua posta su questo dispositivo.",
|
||||
"evicted": "Il browser ha eliminato la posta conservata su questo dispositivo. Viene scaricata di nuovo.",
|
||||
"evicted_lost": "{count, plural, one {# messaggio o modifica non inviati sono andati persi.} other {# messaggi o modifiche non inviati sono andati persi.}}",
|
||||
|
||||
+80
-5
@@ -1976,7 +1976,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "返信元アドレスを自動選択",
|
||||
"description": "返信時に、元のメッセージを受信したIDへ差出人アドレスを自動的に切り替えます"
|
||||
"description": "返信するとき、メッセージが送られてきたアドレスから送信します。"
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "受信アドレスの照合",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "連絡先、カレンダー、フォルダーをサーバーから再読み込みします。サインインしたままです。",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "キャッシュされたデータを更新しますか?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "設定をエクスポート",
|
||||
"description": "設定をJSONとしてダウンロード",
|
||||
"description": "設定のコピーをダウンロード",
|
||||
"button": "エクスポート"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "設定をインポート",
|
||||
"description": "JSONファイルから設定をアップロード",
|
||||
"description": "ファイルから設定を復元",
|
||||
"button": "インポート"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "オフライン",
|
||||
"privacy": "プライバシー",
|
||||
"about": "情報"
|
||||
},
|
||||
"search": {
|
||||
"signature": "署名 メール署名 フッター",
|
||||
"sign_out": "サインアウト ログアウト logout",
|
||||
"app_lock": "PIN ロック コード ロック画面",
|
||||
"text_size": "文字サイズ フォントサイズ 大きい文字 拡大 ズーム",
|
||||
"vacation": "自動返信 不在 休暇 外出中 autoreply",
|
||||
"two_factor": "2FA 二要素 二段階 MFA OTP 認証アプリ ワンタイムコード",
|
||||
"encryption": "S/MIME smime PGP OpenPGP 暗号化 証明書",
|
||||
"password": "パスワード パスワード変更 新しいパスワード",
|
||||
"offline": "オフライン offline 接続なし インターネットなし ダウンロード ストレージ 同期",
|
||||
"wipe": "削除 消去 デバイス データ",
|
||||
"outbox": "送信トレイ 未送信 送信待ち キュー",
|
||||
"remote_content": "外部画像 外部コンテンツ トラッキング ピクセル 信頼できる差出人",
|
||||
"push": "プッシュ push リレー バックグラウンド 通知",
|
||||
"version": "バージョン 更新 情報 ソースコード GitHub ライセンス"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "通知",
|
||||
"push_link": {
|
||||
"label": "通知のリレー",
|
||||
"description": "アプリを閉じているときの通知はプッシュリレーを通りますが、リレーがメールを見ることはありません。通知ページで選択します。",
|
||||
"button": "通知"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "外部画像と信頼できる差出人",
|
||||
"receipts_label": "開封確認",
|
||||
"senders_label": "差出人の画像",
|
||||
"description": "これらの設定はプライバシーページにあります。",
|
||||
"button": "プライバシー"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "表示密度とボタンのラベル",
|
||||
"description": "リストの行の間隔とボタンに名前を表示するかどうかは、外観ページで設定します。"
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "メッセージの明るい背景",
|
||||
"description": "ダークモードでメッセージを明るい背景で表示するかどうかは、閲覧ページで設定します。"
|
||||
},
|
||||
"calendar": {
|
||||
"label": "カレンダーの予定のプレビュー",
|
||||
"description": "予定にポインターを合わせたときに詳細を表示するかどうかは、カレンダーページで設定します。"
|
||||
},
|
||||
"list": {
|
||||
"label": "スレッドとプレビュー行",
|
||||
"description": "スレッドでまとめるかどうかと、各メッセージの下のプレビュー行は、メッセージ一覧ページで設定します。"
|
||||
},
|
||||
"reading": {
|
||||
"label": "既読にする",
|
||||
"description": "メッセージをいつ既読にするか、削除や移動の後にどこへ戻るかは、閲覧ページで設定します。"
|
||||
},
|
||||
"send_warnings_description": "送信の取り消しと、件名なし、添付忘れ、仮の文字列の警告。",
|
||||
"bulk_delete": {
|
||||
"label": "複数のメッセージを削除する前に確認",
|
||||
"description": "これはメッセージ一覧ページで設定します。"
|
||||
},
|
||||
"sounds": {
|
||||
"label": "サウンドと未読バッジ",
|
||||
"description": "通知音とタブの未読数は、通知ページで設定します。"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "{appName} について"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "このデバイスでこのアカウントからサインアウトします。"
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Bulwark Webmail のセットアップ",
|
||||
"subtitle": "ブラウザーから Web メールのインスタンスを設定します。",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, other {未送信のメッセージ#件が失われます。}}",
|
||||
"clear_confirm": "消去",
|
||||
"persist_refused": "空き容量が少なくなると、このブラウザーは保存したメールを削除することがあります。アプリをインストールすると、たいていは防げます。",
|
||||
"persist_retry": "もう一度確認",
|
||||
"persist_retry": "保持するようブラウザーに依頼",
|
||||
"safari_hint": "Safari は、7日間アクセスがないサイトのデータを削除します。このデバイスにメールを残すには、Bulwark をホーム画面または Dock に追加してください。",
|
||||
"evicted": "ブラウザーがこのデバイスに保存していたメールを削除しました。あらためてダウンロードしています。",
|
||||
"evicted_lost": "{count, plural, other {未送信のメッセージまたは変更#件が失われました。}}",
|
||||
|
||||
+80
-5
@@ -1985,7 +1985,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "답장 시 보내는 사람 자동 선택",
|
||||
"description": "답장할 때 메일을 받았던 주소로 보내는 사람을 자동으로 변경해요"
|
||||
"description": "답장할 때 메시지를 받은 주소로 보내요."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "수신 주소 일치 방식",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "서버에서 연락처, 캘린더, 폴더를 다시 불러와요. 로그인 상태는 유지돼요.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "캐시된 데이터를 새로 고칠까요?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "설정 내보내기",
|
||||
"description": "내 설정을 JSON 파일로 다운로드해요",
|
||||
"description": "설정 사본 다운로드",
|
||||
"button": "내보내기"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "설정 가져오기",
|
||||
"description": "JSON 파일에서 설정을 불러와요",
|
||||
"description": "파일에서 설정 복원",
|
||||
"button": "가져오기"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "오프라인",
|
||||
"privacy": "개인정보",
|
||||
"about": "정보"
|
||||
},
|
||||
"search": {
|
||||
"signature": "서명 이메일 서명 바닥글",
|
||||
"sign_out": "로그아웃 로그 아웃 logout 나가기",
|
||||
"app_lock": "PIN 잠금 코드 잠금 화면",
|
||||
"text_size": "글꼴 크기 큰 글자 확대 줌",
|
||||
"vacation": "자동 응답 부재중 휴가 외근 autoreply",
|
||||
"two_factor": "2FA 2단계 2요소 MFA OTP 인증 앱 일회용 코드",
|
||||
"encryption": "S/MIME smime PGP OpenPGP 암호화 인증서",
|
||||
"password": "비밀번호 암호 비밀번호 변경 새 비밀번호",
|
||||
"offline": "오프라인 offline 연결 없음 인터넷 없음 다운로드 저장 공간 동기화",
|
||||
"wipe": "제거 삭제 지우기 기기 데이터",
|
||||
"outbox": "보낼 편지함 보내지 않음 대기 중 대기열",
|
||||
"remote_content": "원격 이미지 외부 콘텐츠 추적 픽셀 신뢰하는 보낸 사람",
|
||||
"push": "푸시 push 릴레이 백그라운드 알림",
|
||||
"version": "버전 업데이트 정보 소스 코드 GitHub 라이선스"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "알림",
|
||||
"push_link": {
|
||||
"label": "알림 릴레이",
|
||||
"description": "앱이 닫혀 있을 때의 알림은 푸시 릴레이를 거치며, 릴레이는 메일을 절대 보지 않아요. 알림 페이지에서 선택해요.",
|
||||
"button": "알림"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "원격 이미지와 신뢰하는 보낸 사람",
|
||||
"receipts_label": "읽음 확인",
|
||||
"senders_label": "보낸 사람 사진",
|
||||
"description": "이 설정은 개인정보 페이지에 있어요.",
|
||||
"button": "개인정보"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "밀도와 버튼 이름",
|
||||
"description": "목록 줄 간격과 버튼에 이름을 표시할지는 모양 페이지에서 설정해요."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "메시지의 밝은 배경",
|
||||
"description": "다크 모드에서 메시지를 밝은 배경으로 보여줄지는 읽기 페이지에서 설정해요."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "캘린더의 일정 미리 보기",
|
||||
"description": "일정을 가리킬 때 자세한 내용을 보여줄지는 캘린더 페이지에서 설정해요."
|
||||
},
|
||||
"list": {
|
||||
"label": "대화와 미리 보기 줄",
|
||||
"description": "대화별 묶기와 각 메시지 아래의 미리 보기 줄은 메시지 목록 페이지에서 설정해요."
|
||||
},
|
||||
"reading": {
|
||||
"label": "읽음으로 표시",
|
||||
"description": "메시지를 언제 읽음으로 볼지, 삭제하거나 옮긴 뒤 어디로 갈지는 읽기 페이지에서 설정해요."
|
||||
},
|
||||
"send_warnings_description": "보내기 취소, 그리고 제목 없음, 첨부 파일 누락, 자리 표시 텍스트 경고.",
|
||||
"bulk_delete": {
|
||||
"label": "여러 메시지를 삭제하기 전에 묻기",
|
||||
"description": "메시지 목록 페이지에서 설정해요."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "소리와 읽지 않음 배지",
|
||||
"description": "알림 소리와 탭의 읽지 않은 개수는 알림 페이지에서 설정해요."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "{appName} 정보"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "이 기기에서 이 계정을 로그아웃해요."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Bulwark Webmail 설정",
|
||||
"subtitle": "브라우저에서 웹메일 인스턴스를 설정하세요.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, other {보내지 않은 메시지 #개가 사라집니다.}}",
|
||||
"clear_confirm": "비우기",
|
||||
"persist_refused": "저장 공간이 부족해지면 이 브라우저가 보관한 메일을 삭제할 수 있습니다. 앱을 설치하면 대개 막을 수 있습니다.",
|
||||
"persist_retry": "다시 요청",
|
||||
"persist_retry": "브라우저에 보관을 요청",
|
||||
"safari_hint": "Safari는 7일 동안 방문하지 않은 사이트의 데이터를 삭제합니다. 이 기기에 메일을 계속 보관하려면 Bulwark를 홈 화면이나 Dock에 추가하세요.",
|
||||
"evicted": "브라우저가 이 기기에 보관한 메일을 삭제했습니다. 다시 다운로드하는 중입니다.",
|
||||
"evicted_lost": "{count, plural, other {보내지 않은 메시지 또는 변경 사항 #개가 사라졌습니다.}}",
|
||||
|
||||
+80
-5
@@ -1976,7 +1976,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Automātiski izvēlēties atbildes adresi",
|
||||
"description": "Atbildot automātiski izmantot to kontu, uz kuru vēstule tika saņemta"
|
||||
"description": "Atbildot sūtīt no adreses, uz kuru vēstule tika nosūtīta."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Saņemšanas adreses atbilstība",
|
||||
@@ -2610,7 +2610,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "Vēlreiz ielādēt kontaktus, kalendārus un mapes no servera. Jūs paliekat pierakstījies.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "Atsvaidzināt kešatmiņas datus?"
|
||||
},
|
||||
@@ -2623,12 +2623,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Eksportēt iestatījumus",
|
||||
"description": "Lejupielādēt iestatījumus JSON formātā",
|
||||
"description": "Lejupielādēt iestatījumu kopiju",
|
||||
"button": "Eksportēt"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Importēt iestatījumus",
|
||||
"description": "Augšupielādēt iestatījumus no JSON faila",
|
||||
"description": "Atjaunot iestatījumus no faila",
|
||||
"button": "Importēt"
|
||||
},
|
||||
"debug_categories": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Bezsaistē",
|
||||
"privacy": "Privātums",
|
||||
"about": "Par"
|
||||
},
|
||||
"search": {
|
||||
"signature": "paraksts e-pasta paraksts kājene",
|
||||
"sign_out": "izrakstīties iziet logout",
|
||||
"app_lock": "PIN slēdzene kods bloķēšanas ekrāns",
|
||||
"text_size": "fonta izmērs lielāks teksts tālummaiņa palielināt",
|
||||
"vacation": "automātiskā atbilde ārpus biroja prombūtne atvaļinājums autoreply",
|
||||
"two_factor": "2FA divpakāpju divfaktoru MFA OTP autentifikators vienreizējs kods",
|
||||
"encryption": "S/MIME smime PGP OpenPGP šifrēšana sertifikāts",
|
||||
"password": "parole mainīt paroli jauna parole",
|
||||
"offline": "bezsaistē offline bez savienojuma bez interneta lejupielādēt krātuve sinhronizēt",
|
||||
"wipe": "noņemt dzēst notīrīt ierīce dati",
|
||||
"outbox": "izsūtne nenosūtīts gaida rinda",
|
||||
"remote_content": "attāli attēli ārējs saturs izsekošana pikseļi uzticami sūtītāji",
|
||||
"push": "push retranslators fonā paziņojumi",
|
||||
"version": "versija atjauninājums par pirmkods GitHub licence"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Paziņojumi",
|
||||
"push_link": {
|
||||
"label": "Paziņojumu retranslators",
|
||||
"description": "Paziņojumi, kamēr lietotne ir aizvērta, iet caur push retranslatoru, kas nekad neredz jūsu pastu. To izvēlaties lapā Paziņojumi.",
|
||||
"button": "Paziņojumi"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Attāli attēli un uzticami sūtītāji",
|
||||
"receipts_label": "Izlasīšanas apstiprinājumi",
|
||||
"senders_label": "Sūtītāju attēli",
|
||||
"description": "Šie iestatījumi ir lapā Privātums.",
|
||||
"button": "Privātums"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Blīvums un pogu nosaukumi",
|
||||
"description": "Cik cieši ir saraksta rindas un vai pogas rāda nosaukumu, iestatāt lapā Izskats."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Gaišs fons vēstulēm",
|
||||
"description": "Vēstuļu rādīšanu uz gaiša fona tumšajā režīmā iestatāt lapā Lasīšana."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Notikumu priekšskatījums kalendārā",
|
||||
"description": "Vai, norādot uz notikumu, tiek rādīta informācija, iestatāt lapā Kalendārs."
|
||||
},
|
||||
"list": {
|
||||
"label": "Sarunas un priekšskatījuma rindas",
|
||||
"description": "Grupēšanu pa sarunām un priekšskatījuma rindu zem katras vēstules iestatāt lapā Vēstuļu saraksts."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Atzīmēt kā izlasītu",
|
||||
"description": "Kad vēstule skaitās izlasīta un kur nonākat pēc dzēšanas vai pārvietošanas, iestatāt lapā Lasīšana."
|
||||
},
|
||||
"send_warnings_description": "Sūtīšanas atsaukšana un brīdinājumi par trūkstošu tēmu, aizmirstu pielikumu vai vietturu tekstu.",
|
||||
"bulk_delete": {
|
||||
"label": "Jautāt pirms vairāku vēstuļu dzēšanas",
|
||||
"description": "To iestatāt lapā Vēstuļu saraksts."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Skaņas un nelasīto skaits",
|
||||
"description": "Paziņojumu skaņas un nelasīto skaitu cilnē iestatāt lapā Paziņojumi."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Par {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Izrakstīties no šī konta šajā ierīcē."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Bulwark Webmail iestatīšana",
|
||||
"subtitle": "Iestatiet savu tīmekļa pasta instanci pārlūkā.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, zero {# neizsūtītu vēstuļu tiks zaudēts.} one {Tiks zaudēta # neizsūtīta vēstule.} other {Tiks zaudētas # neizsūtītas vēstules.}}",
|
||||
"clear_confirm": "Notīrīt",
|
||||
"persist_refused": "Šis pārlūks var izdzēst glabāto e-pastu, kad pietrūkst vietas. Lietotnes instalēšana to parasti novērš.",
|
||||
"persist_retry": "Pajautāt vēlreiz",
|
||||
"persist_retry": "Lūgt pārlūku to saglabāt",
|
||||
"safari_hint": "Safari izdzēš vietnes datus pēc 7 dienām bez apmeklējuma. Pievienojiet Bulwark sākuma ekrānam vai Dock, lai e-pasts paliktu šajā ierīcē.",
|
||||
"evicted": "Pārlūks izdzēsa šajā ierīcē glabāto e-pastu. Tas tiek lejupielādēts no jauna.",
|
||||
"evicted_lost": "{count, plural, zero {Tika zaudētas # neizsūtītu vēstuļu vai izmaiņu.} one {Tika zaudēta # neizsūtīta vēstule vai izmaiņa.} other {Tika zaudētas # neizsūtītas vēstules vai izmaiņas.}}",
|
||||
|
||||
+80
-5
@@ -1988,7 +1988,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Хүлээн авсан хаягаас хариу бичих",
|
||||
"description": "Хариу бичихдээ мессежийг анх илгээсэн хаягаар нь илгээнэ үү. Эхлээд таних тэмдэгтэй таарах; Домэйн бүх хүргэлтийн хувьд, таны үндсэн таниулбараар дамжуулан илгээхдээ From толгой хэсгийг өөр нэр рүү дахин бичнэ."
|
||||
"description": "Хариу бичихдээ захидал ирсэн хаягаас илгээнэ."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Хүлээн авах хаягийн тохирол",
|
||||
@@ -2636,7 +2636,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Кэш хийсэн өгөгдлийг сэргээх",
|
||||
"description": "Харилцагч, хуанли, фолдеруудыг серверээс дахин ачаална уу. Таны бүртгэл болон сешнүүдийг хадгална — гаралгүй хуучирсан эсвэл буруу харагдацыг засна.",
|
||||
"description": "Харилцагч, хуанли, хавтсуудыг серверээс дахин ачаална. Та нэвтэрсэн хэвээр үлдэнэ.",
|
||||
"button": "Сэргээх",
|
||||
"confirm_title": "Кэшлэгдсэн өгөгдлийг шинэчлэх үү?"
|
||||
},
|
||||
@@ -2649,12 +2649,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Экспортын тохиргоо",
|
||||
"description": "Тохиргоогоо JSON",
|
||||
"description": "Тохиргооныхоо хуулбарыг татах",
|
||||
"button": "Экспорт"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Импортын тохиргоо",
|
||||
"description": "JSON файлаас тохиргоог байршуулах",
|
||||
"description": "Тохиргоог файлаас сэргээх",
|
||||
"button": "Импорт"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Офлайн",
|
||||
"privacy": "Нууцлал",
|
||||
"about": "Тухай"
|
||||
},
|
||||
"search": {
|
||||
"signature": "гарын үсэг имэйлийн гарын үсэг хөл хэсэг",
|
||||
"sign_out": "гарах системээс гарах logout",
|
||||
"app_lock": "PIN түгжээ код түгжих дэлгэц",
|
||||
"text_size": "үсгийн хэмжээ том бичиг томруулах zoom",
|
||||
"vacation": "автомат хариу оффисоос гадуур эзгүй амралт autoreply",
|
||||
"two_factor": "2FA хоёр шатлалт хоёр хүчин зүйлт MFA OTP баталгаажуулагч нэг удаагийн код",
|
||||
"encryption": "S/MIME smime PGP OpenPGP шифрлэлт гэрчилгээ",
|
||||
"password": "нууц үг нууц үг солих шинэ нууц үг",
|
||||
"offline": "офлайн offline холболтгүй интернэтгүй татах хадгалах сан синк",
|
||||
"wipe": "устгах арилгах цэвэрлэх төхөөрөмж өгөгдөл",
|
||||
"outbox": "илгээх хайрцаг илгээгээгүй хүлээгдэж буй дараалал",
|
||||
"remote_content": "алсын зураг гадаад агуулга мөрдөх пиксел итгэмжлэгдсэн илгээгч",
|
||||
"push": "push реле ар тал мэдэгдэл",
|
||||
"version": "хувилбар шинэчлэл тухай эх код GitHub лиценз"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Мэдэгдэл",
|
||||
"push_link": {
|
||||
"label": "Мэдэгдлийн реле",
|
||||
"description": "Апп хаалттай үеийн мэдэгдэл push релеээр дамждаг бөгөөд реле таны захидлыг хэзээ ч хардаггүй. Үүнийг Мэдэгдэл хуудсанд сонгоно.",
|
||||
"button": "Мэдэгдэл"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Алсын зураг ба итгэмжлэгдсэн илгээгчид",
|
||||
"receipts_label": "Уншсан баталгаа",
|
||||
"senders_label": "Илгээгчийн зураг",
|
||||
"description": "Эдгээр тохиргоо Нууцлал хуудсанд байна.",
|
||||
"button": "Нууцлал"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Нягтрал ба товчны нэр",
|
||||
"description": "Жагсаалтын мөрүүдийн зай болон товчнууд нэрээ харуулах эсэхийг Харагдах байдал хуудсанд тохируулна."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Захидлын цайвар дэвсгэр",
|
||||
"description": "Бараан горимд захидлыг цайвар дэвсгэр дээр харуулахыг Унших хуудсанд тохируулна."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Хуанли дахь үйл явдлын урьдчилсан харагдац",
|
||||
"description": "Үйл явдал руу заахад дэлгэрэнгүйг харуулах эсэхийг Хуанли хуудсанд тохируулна."
|
||||
},
|
||||
"list": {
|
||||
"label": "Харилцан яриа ба урьдчилсан мөр",
|
||||
"description": "Харилцан яриагаар бүлэглэх болон захидал бүрийн доорх урьдчилсан мөрийг Захидлын жагсаалт хуудсанд тохируулна."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Уншсан гэж тэмдэглэх",
|
||||
"description": "Захидал хэзээ уншсанд тооцогдох, устгах эсвэл зөөсний дараа хаашаа очихыг Унших хуудсанд тохируулна."
|
||||
},
|
||||
"send_warnings_description": "Илгээхийг буцаах, мөн гарчиггүй, хавсралт мартсан эсвэл орлуулагч бичвэрийн анхааруулга.",
|
||||
"bulk_delete": {
|
||||
"label": "Олон захидал устгахаас өмнө асуух",
|
||||
"description": "Үүнийг Захидлын жагсаалт хуудсанд тохируулна."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Дуу ба уншаагүйн тоолуур",
|
||||
"description": "Мэдэгдлийн дуу болон таб дээрх уншаагүйн тоог Мэдэгдэл хуудсанд тохируулна."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "{appName}-ийн тухай"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Энэ төхөөрөмж дээр энэ бүртгэлээс гарах."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Bulwark Webmail-ийн тохиргоо",
|
||||
"subtitle": "Вэб шуудангийн инстансаа хөтчөөс тохируулна уу.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# илгээгээгүй мессеж алдагдана.} other {# илгээгээгүй мессеж алдагдана.}}",
|
||||
"clear_confirm": "Цэвэрлэх",
|
||||
"persist_refused": "Зай дутвал энэ хөтөч хадгалсан имэйлийг устгаж болно. Аппыг суулгавал ихэвчлэн ийм зүйл болдоггүй.",
|
||||
"persist_retry": "Дахин асуух",
|
||||
"persist_retry": "Хөтчөөс хадгалахыг хүсэх",
|
||||
"safari_hint": "Safari сайтад 7 хоног зочлоогүй бол түүний өгөгдлийг устгадаг. Имэйлээ энэ төхөөрөмж дээр хадгалахын тулд Bulwark-ыг Үндсэн дэлгэц эсвэл Dock-д нэмнэ үү.",
|
||||
"evicted": "Хөтөч энэ төхөөрөмж дээр хадгалсан имэйлийг устгасан. Дахин татаж байна.",
|
||||
"evicted_lost": "{count, plural, one {# илгээгээгүй мессеж эсвэл өөрчлөлт алдагдсан.} other {# илгээгээгүй мессеж эсвэл өөрчлөлт алдагдсан.}}",
|
||||
|
||||
+80
-5
@@ -1988,7 +1988,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Svar fra mottatt adresse",
|
||||
"description": "Når du svarer, bruker Bulwark adressen meldingen opprinnelig ble sendt til. Programmet ser først etter en samsvarende identitet. For meldinger til et oppsamlingsdomene brukes aliaset i Fra-feltet, mens sendingen går gjennom hovedidentiteten din."
|
||||
"description": "Når du svarer, send fra adressen meldingen ble sendt til."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Samsvar for mottaksadresse",
|
||||
@@ -2636,7 +2636,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Oppdater bufrede data",
|
||||
"description": "Last inn kontakter, kalendere og mapper på nytt fra serveren. Kontoene og øktene beholdes, slik at du kan rette en utdatert eller feil visning uten å logge av.",
|
||||
"description": "Last inn kontakter, kalendere og mapper fra serveren på nytt. Du forblir logget inn.",
|
||||
"button": "Oppdater",
|
||||
"confirm_title": "Oppdatere bufrede data?"
|
||||
},
|
||||
@@ -2649,12 +2649,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Eksporter innstillinger",
|
||||
"description": "Last ned innstillingene dine som JSON",
|
||||
"description": "Last ned en kopi av innstillingene",
|
||||
"button": "Eksporter"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Importer innstillinger",
|
||||
"description": "Last opp innstillinger fra en JSON-fil",
|
||||
"description": "Gjenopprett innstillinger fra en fil",
|
||||
"button": "Importer"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Frakoblet",
|
||||
"privacy": "Personvern",
|
||||
"about": "Om"
|
||||
},
|
||||
"search": {
|
||||
"signature": "signatur e-postsignatur bunntekst",
|
||||
"sign_out": "logg ut logout avlogging",
|
||||
"app_lock": "PIN lås kode låseskjerm",
|
||||
"text_size": "skriftstørrelse større tekst zoom forstørr",
|
||||
"vacation": "autosvar automatisk svar fraværende ferie ikke på kontoret autoreply",
|
||||
"two_factor": "2FA totrinns tofaktor MFA OTP autentiseringsapp engangskode",
|
||||
"encryption": "S/MIME smime PGP OpenPGP kryptering sertifikat",
|
||||
"password": "passord endre passord nytt passord",
|
||||
"offline": "frakoblet offline uten tilkobling uten internett last ned lagring synkroniser",
|
||||
"wipe": "fjern slett tøm enhet data",
|
||||
"outbox": "utboks ikke sendt venter kø",
|
||||
"remote_content": "eksterne bilder eksternt innhold sporing piksler klarerte avsendere",
|
||||
"push": "push relé bakgrunn varsler",
|
||||
"version": "versjon oppdatering om kildekode GitHub lisens"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Varsler",
|
||||
"push_link": {
|
||||
"label": "Relé for varsler",
|
||||
"description": "Varsler mens appen er lukket går gjennom et push-relé, som aldri ser e-posten din. Du velger det på siden Varsler.",
|
||||
"button": "Varsler"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Eksterne bilder og klarerte avsendere",
|
||||
"receipts_label": "Lesebekreftelser",
|
||||
"senders_label": "Avsenderbilder",
|
||||
"description": "Disse innstillingene finner du på siden Personvern.",
|
||||
"button": "Personvern"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Tetthet og knappenavn",
|
||||
"description": "Hvor tett radene i listen står, og om knappene viser navnet sitt, stiller du inn på siden Utseende."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Lys bakgrunn for meldinger",
|
||||
"description": "Å vise meldinger på lys bakgrunn i mørk modus stiller du inn på siden Lesing."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Forhåndsvisning av hendelser i kalenderen",
|
||||
"description": "Om det å peke på en hendelse viser detaljene, stiller du inn på siden Kalender."
|
||||
},
|
||||
"list": {
|
||||
"label": "Samtaler og forhåndsvisningslinjer",
|
||||
"description": "Gruppering etter samtale og forhåndsvisningslinjen under hver melding stiller du inn på siden Meldingsliste."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Merke som lest",
|
||||
"description": "Når en melding regnes som lest, og hvor du havner etter å ha slettet eller flyttet den, stiller du inn på siden Lesing."
|
||||
},
|
||||
"send_warnings_description": "Angre sending og varslene om manglende emne, glemt vedlegg eller plassholdertekst.",
|
||||
"bulk_delete": {
|
||||
"label": "Spør før sletting av flere meldinger",
|
||||
"description": "Dette stiller du inn på siden Meldingsliste."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Lyder og telleren for uleste",
|
||||
"description": "Varsellyder og antall uleste i fanen stiller du inn på siden Varsler."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Om {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Logg ut av denne kontoen på denne enheten."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Oppsett av Bulwark Webmail",
|
||||
"subtitle": "Sett opp webmail-instansen din i nettleseren.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# usendt melding går tapt.} other {# usendte meldinger går tapt.}}",
|
||||
"clear_confirm": "Tøm",
|
||||
"persist_refused": "Denne nettleseren kan slette den beholdte e-posten når plassen blir knapp. Å installere appen hjelper vanligvis.",
|
||||
"persist_retry": "Spør igjen",
|
||||
"persist_retry": "Be nettleseren om å beholde den",
|
||||
"safari_hint": "Safari sletter data fra et nettsted etter 7 dager uten besøk. Legg Bulwark til på Hjem-skjermen eller i Dock for å beholde e-posten på denne enheten.",
|
||||
"evicted": "Nettleseren slettet e-posten som var beholdt på denne enheten. Den lastes ned på nytt.",
|
||||
"evicted_lost": "{count, plural, one {# usendt melding eller endring gikk tapt.} other {# usendte meldinger eller endringer gikk tapt.}}",
|
||||
|
||||
+80
-5
@@ -1976,7 +1976,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Antwoordadres automatisch selecteren",
|
||||
"description": "Schakel bij het beantwoorden automatisch het Van-adres om naar de identiteit die het oorspronkelijke bericht ontving"
|
||||
"description": "Bij beantwoorden verzenden vanaf het adres waarnaar het bericht is gestuurd."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Overeenkomst van ontvangstadres",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "Contacten, agenda’s en mappen opnieuw van de server laden. U blijft aangemeld.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "Gegevens in de cache vernieuwen?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Instellingen exporteren",
|
||||
"description": "Download je instellingen als JSON",
|
||||
"description": "Een kopie van uw instellingen downloaden",
|
||||
"button": "Exporteren"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Instellingen importeren",
|
||||
"description": "Upload instellingen vanuit JSON-bestand",
|
||||
"description": "Instellingen herstellen uit een bestand",
|
||||
"button": "Importeren"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Offline",
|
||||
"privacy": "Privacy",
|
||||
"about": "Over"
|
||||
},
|
||||
"search": {
|
||||
"signature": "handtekening e-mailhandtekening ondertekening voettekst",
|
||||
"sign_out": "afmelden uitloggen logout",
|
||||
"app_lock": "PIN vergrendeling code vergrendelscherm",
|
||||
"text_size": "lettergrootte grotere tekst zoom vergroten",
|
||||
"vacation": "automatisch antwoord afwezig vakantie buiten kantoor autoreply",
|
||||
"two_factor": "2FA tweestaps twee-factor MFA OTP authenticator eenmalige code",
|
||||
"encryption": "S/MIME smime PGP OpenPGP versleuteling certificaat",
|
||||
"password": "wachtwoord wijzigen nieuw wachtwoord",
|
||||
"offline": "offline zonder verbinding zonder internet downloaden opslag synchroniseren",
|
||||
"wipe": "verwijderen wissen apparaat gegevens",
|
||||
"outbox": "postvak uit niet verzonden wachtend wachtrij",
|
||||
"remote_content": "externe afbeeldingen externe inhoud tracking pixels vertrouwde afzenders",
|
||||
"push": "push relay achtergrond meldingen",
|
||||
"version": "versie update over broncode GitHub licentie"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Meldingen",
|
||||
"push_link": {
|
||||
"label": "Relay voor meldingen",
|
||||
"description": "Meldingen terwijl de app dicht is gaan via een push-relay, die uw mail nooit ziet. U kiest die op de pagina Meldingen.",
|
||||
"button": "Meldingen"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Externe afbeeldingen en vertrouwde afzenders",
|
||||
"receipts_label": "Leesbevestigingen",
|
||||
"senders_label": "Afzenderafbeeldingen",
|
||||
"description": "Deze instellingen staan op de pagina Privacy.",
|
||||
"button": "Privacy"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Dichtheid en knoplabels",
|
||||
"description": "Hoe dicht de rijen in de lijst staan en of knoppen hun naam tonen, stelt u in op de pagina Weergave."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Lichte achtergrond voor berichten",
|
||||
"description": "Berichten op een lichte achtergrond tonen in donkere modus stelt u in op de pagina Lezen."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Voorbeeld van afspraken in de agenda",
|
||||
"description": "Of wijzen naar een afspraak de details toont, stelt u in op de pagina Agenda."
|
||||
},
|
||||
"list": {
|
||||
"label": "Gesprekken en voorbeeldregels",
|
||||
"description": "Groeperen per gesprek en de voorbeeldregel onder elk bericht stelt u in op de pagina Berichtenlijst."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Als gelezen markeren",
|
||||
"description": "Wanneer een bericht als gelezen telt, en waar u na verwijderen of verplaatsen heen gaat, stelt u in op de pagina Lezen."
|
||||
},
|
||||
"send_warnings_description": "Verzenden ongedaan maken en de waarschuwingen voor een ontbrekend onderwerp, een vergeten bijlage of opvultekst.",
|
||||
"bulk_delete": {
|
||||
"label": "Vragen voor het verwijderen van meerdere berichten",
|
||||
"description": "Dit stelt u in op de pagina Berichtenlijst."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Geluiden en de teller voor ongelezen",
|
||||
"description": "Meldingsgeluiden en het aantal ongelezen op het tabblad stelt u in op de pagina Meldingen."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Over {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Dit account op dit apparaat afmelden."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Bulwark Webmail instellen",
|
||||
"subtitle": "Stel je webmailinstantie in vanuit de browser.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# niet-verzonden bericht gaat verloren.} other {# niet-verzonden berichten gaan verloren.}}",
|
||||
"clear_confirm": "Wissen",
|
||||
"persist_refused": "Deze browser kan de bewaarde e-mail verwijderen als de ruimte opraakt. De app installeren voorkomt dat meestal.",
|
||||
"persist_retry": "Opnieuw vragen",
|
||||
"persist_retry": "Browser vragen het te bewaren",
|
||||
"safari_hint": "Safari verwijdert de gegevens van een site na 7 dagen zonder bezoek. Zet Bulwark op je beginscherm of in het Dock om je e-mail op dit apparaat te houden.",
|
||||
"evicted": "De browser heeft de e-mail verwijderd die op dit apparaat was bewaard. Ze wordt opnieuw gedownload.",
|
||||
"evicted_lost": "{count, plural, one {# niet-verzonden bericht of wijziging is verloren gegaan.} other {# niet-verzonden berichten of wijzigingen zijn verloren gegaan.}}",
|
||||
|
||||
+80
-5
@@ -1985,7 +1985,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Automatycznie wybieraj adres odpowiedzi",
|
||||
"description": "Podczas odpowiadania automatycznie przełączaj adres nadawcy na tożsamość, która pierwotnie otrzymała wiadomość"
|
||||
"description": "Przy odpowiadaniu wysyłaj z adresu, na który wysłano wiadomość."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Dopasowanie adresu odbioru",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "Ponownie wczytaj kontakty, kalendarze i foldery z serwera. Pozostajesz zalogowany.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "Odświeżyć dane w pamięci podręcznej?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Eksportuj ustawienia",
|
||||
"description": "Pobierz swoje ustawienia jako JSON",
|
||||
"description": "Pobierz kopię ustawień",
|
||||
"button": "Eksportuj"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Importuj ustawienia",
|
||||
"description": "Prześlij ustawienia z pliku JSON",
|
||||
"description": "Przywróć ustawienia z pliku",
|
||||
"button": "Importuj"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Offline",
|
||||
"privacy": "Prywatność",
|
||||
"about": "Informacje"
|
||||
},
|
||||
"search": {
|
||||
"signature": "podpis podpis e-mail stopka",
|
||||
"sign_out": "wyloguj wylogowanie logout",
|
||||
"app_lock": "PIN blokada kod zablokuj ekran",
|
||||
"text_size": "rozmiar czcionki większy tekst powiększenie zoom",
|
||||
"vacation": "autoodpowiedź automatyczna odpowiedź poza biurem nieobecność urlop autoreply",
|
||||
"two_factor": "2FA dwuetapowe dwuskładnikowe MFA OTP uwierzytelniacz kod jednorazowy",
|
||||
"encryption": "S/MIME smime PGP OpenPGP szyfrowanie certyfikat",
|
||||
"password": "hasło zmień hasło nowe hasło",
|
||||
"offline": "offline bez połączenia bez internetu pobierz pamięć synchronizacja",
|
||||
"wipe": "usuń wyczyść wymaż urządzenie dane",
|
||||
"outbox": "skrzynka nadawcza niewysłane oczekujące kolejka",
|
||||
"remote_content": "zdalne obrazy treści zewnętrzne śledzenie piksele zaufani nadawcy",
|
||||
"push": "push przekaźnik w tle powiadomienia",
|
||||
"version": "wersja aktualizacja informacje kod źródłowy GitHub licencja"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Powiadomienia",
|
||||
"push_link": {
|
||||
"label": "Przekaźnik powiadomień",
|
||||
"description": "Powiadomienia przy zamkniętej aplikacji idą przez przekaźnik push, który nigdy nie widzi Twojej poczty. Wybierasz go na stronie Powiadomienia.",
|
||||
"button": "Powiadomienia"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Zdalne obrazy i zaufani nadawcy",
|
||||
"receipts_label": "Potwierdzenia przeczytania",
|
||||
"senders_label": "Obrazy nadawców",
|
||||
"description": "Te ustawienia są na stronie Prywatność.",
|
||||
"button": "Prywatność"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Gęstość i etykiety paska",
|
||||
"description": "Odstępy między wierszami listy i to, czy przyciski pokazują nazwy, ustawisz na stronie Wygląd."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Jasne tło wiadomości",
|
||||
"description": "Wyświetlanie wiadomości na jasnym tle w trybie ciemnym ustawisz na stronie Czytanie."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Podgląd wydarzeń w kalendarzu",
|
||||
"description": "To, czy wskazanie wydarzenia pokazuje szczegóły, ustawisz na stronie Kalendarz."
|
||||
},
|
||||
"list": {
|
||||
"label": "Wątki i wiersze podglądu",
|
||||
"description": "Grupowanie w wątki i wiersz podglądu pod każdą wiadomością ustawisz na stronie Lista wiadomości."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Oznaczanie jako przeczytane",
|
||||
"description": "Kiedy wiadomość liczy się jako przeczytana i dokąd trafiasz po usunięciu lub przeniesieniu, ustawisz na stronie Czytanie."
|
||||
},
|
||||
"send_warnings_description": "Cofnięcie wysłania i ostrzeżenia o braku tematu, zapomnianym załączniku lub tekście zastępczym.",
|
||||
"bulk_delete": {
|
||||
"label": "Pytanie przed usunięciem wielu wiadomości",
|
||||
"description": "To ustawisz na stronie Lista wiadomości."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Dźwięki i licznik nieprzeczytanych",
|
||||
"description": "Dźwięki powiadomień i liczbę nieprzeczytanych na karcie ustawisz na stronie Powiadomienia."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Informacje o {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Wyloguj to konto na tym urządzeniu."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Konfiguracja Bulwark Webmail",
|
||||
"subtitle": "Skonfiguruj swoją instancję poczty w przeglądarce.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# niewysłana wiadomość zostanie utracona.} few {# niewysłane wiadomości zostaną utracone.} many {# niewysłanych wiadomości zostanie utraconych.} other {# niewysłanej wiadomości zostanie utraconej.}}",
|
||||
"clear_confirm": "Wyczyść",
|
||||
"persist_refused": "Ta przeglądarka może usunąć przechowywaną pocztę, gdy zabraknie miejsca. Zainstalowanie aplikacji zwykle temu zapobiega.",
|
||||
"persist_retry": "Zapytaj ponownie",
|
||||
"persist_retry": "Poproś przeglądarkę o zachowanie",
|
||||
"safari_hint": "Safari usuwa dane witryny po 7 dniach bez odwiedzin. Dodaj Bulwark do ekranu początkowego lub do Docka, aby zachować pocztę na tym urządzeniu.",
|
||||
"evicted": "Przeglądarka usunęła pocztę przechowywaną na tym urządzeniu. Jest pobierana ponownie.",
|
||||
"evicted_lost": "{count, plural, one {Utracono # niewysłaną wiadomość lub zmianę.} few {Utracono # niewysłane wiadomości lub zmiany.} many {Utracono # niewysłanych wiadomości lub zmian.} other {Utracono # niewysłanej wiadomości lub zmiany.}}",
|
||||
|
||||
+80
-5
@@ -1976,7 +1976,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Selecionar automaticamente o endereço de resposta",
|
||||
"description": "Ao responder, muda automaticamente o endereço do remetente para a identidade que recebeu a mensagem original"
|
||||
"description": "Ao responder, enviar do endereço para o qual a mensagem foi enviada."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Correspondência do endereço de receção",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Atualizar dados armazenados em cache",
|
||||
"description": "Atualize os contatos, calendários e pastas do servidor. Mantém suas contas e sessões — corrige uma visualização desatualizada ou incorreta sem precisar sair da conta.",
|
||||
"description": "Recarregar contatos, calendários e pastas do servidor. Você continua conectado.",
|
||||
"button": "Recarregar",
|
||||
"confirm_title": "Atualizar os dados em cache?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Exportar Configurações",
|
||||
"description": "Baixar suas configurações como JSON",
|
||||
"description": "Baixar uma cópia das suas configurações",
|
||||
"button": "Exportar"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Importar Configurações",
|
||||
"description": "Fazer upload de configurações de arquivo JSON",
|
||||
"description": "Restaurar as configurações de um arquivo",
|
||||
"button": "Importar"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Offline",
|
||||
"privacy": "Privacidade",
|
||||
"about": "Sobre"
|
||||
},
|
||||
"search": {
|
||||
"signature": "assinatura assinatura de email rodapé",
|
||||
"sign_out": "sair terminar sessão desconectar logout",
|
||||
"app_lock": "PIN bloqueio código bloquear ecrã tela",
|
||||
"text_size": "tamanho da fonte texto maior zoom ampliar",
|
||||
"vacation": "resposta automática fora do escritório ausente férias autoreply",
|
||||
"two_factor": "2FA dois fatores duas etapas MFA OTP autenticador código único",
|
||||
"encryption": "S/MIME smime PGP OpenPGP criptografia certificado",
|
||||
"password": "senha palavra-passe alterar senha nova senha",
|
||||
"offline": "offline sem conexão sem internet baixar armazenamento sincronizar",
|
||||
"wipe": "remover apagar excluir dispositivo dados",
|
||||
"outbox": "caixa de saída não enviado em espera fila",
|
||||
"remote_content": "imagens remotas conteúdo externo rastreamento pixels remetentes confiáveis",
|
||||
"push": "push retransmissor segundo plano notificações",
|
||||
"version": "versão atualização sobre código-fonte GitHub licença"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Notificações",
|
||||
"push_link": {
|
||||
"label": "Retransmissor de notificações",
|
||||
"description": "As notificações com o app fechado passam por um retransmissor push, que nunca vê seus e-mails. Você o escolhe na página Notificações.",
|
||||
"button": "Notificações"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Imagens remotas e remetentes confiáveis",
|
||||
"receipts_label": "Confirmações de leitura",
|
||||
"senders_label": "Imagens dos remetentes",
|
||||
"description": "Estas configurações estão na página Privacidade.",
|
||||
"button": "Privacidade"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Densidade e rótulos da barra",
|
||||
"description": "O espaçamento das linhas da lista e se os botões mostram o nome são definidos na página Aparência."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Fundo claro para as mensagens",
|
||||
"description": "Mostrar as mensagens com fundo claro no modo escuro é definido na página Leitura."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Pré-visualização de eventos no calendário",
|
||||
"description": "Se apontar para um evento mostra os detalhes é definido na página Calendário."
|
||||
},
|
||||
"list": {
|
||||
"label": "Conversas e linhas de pré-visualização",
|
||||
"description": "O agrupamento por conversa e a linha de pré-visualização de cada mensagem são definidos na página Lista de mensagens."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Marcar como lido",
|
||||
"description": "Quando uma mensagem conta como lida, e para onde você vai depois de apagá-la ou movê-la, é definido na página Leitura."
|
||||
},
|
||||
"send_warnings_description": "Desfazer envio e os avisos de assunto vazio, anexo esquecido ou texto provisório.",
|
||||
"bulk_delete": {
|
||||
"label": "Perguntar antes de apagar várias mensagens",
|
||||
"description": "Isto é definido na página Lista de mensagens."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Sons e contador de não lidas",
|
||||
"description": "Os sons de notificação e o número de não lidas na aba são definidos na página Notificações."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Sobre o {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Sair desta conta neste dispositivo."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Configuração do Bulwark Webmail",
|
||||
"subtitle": "Configure a sua instância de webmail a partir do navegador.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# mensagem por enviar será perdida.} other {# mensagens por enviar serão perdidas.}}",
|
||||
"clear_confirm": "Limpar",
|
||||
"persist_refused": "Este navegador pode eliminar o correio mantido quando o espaço escasseia. Instalar a aplicação costuma evitá-lo.",
|
||||
"persist_retry": "Perguntar novamente",
|
||||
"persist_retry": "Pedir ao navegador para manter",
|
||||
"safari_hint": "O Safari elimina os dados de um site após 7 dias sem visitas. Adicione o Bulwark ao Ecrã Principal ou ao Dock para manter o seu correio neste dispositivo.",
|
||||
"evicted": "O navegador eliminou o correio mantido neste dispositivo. Está a ser transferido novamente.",
|
||||
"evicted_lost": "{count, plural, one {# mensagem ou alteração por enviar foi perdida.} other {# mensagens ou alterações por enviar foram perdidas.}}",
|
||||
|
||||
+80
-5
@@ -1988,7 +1988,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Răspunde de la adresa de la care a fost primit mesajul",
|
||||
"description": "Când răspundeți, trimiteți mesajul de la adresa la care a fost trimis inițial. Se potrivesc mai întâi identitățile; pentru livrările de tip „catch-all” ale domeniului, antetul „De la” este rescris cu aliasul, în timp ce trimiterea se face prin identitatea dvs. principală."
|
||||
"description": "Când răspundeți, trimiteți de la adresa la care a fost trimis mesajul."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Potrivirea adresei de primire",
|
||||
@@ -2636,7 +2636,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "Reîncărcați contactele, calendarele și dosarele de pe server. Rămâneți conectat.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "Reîmprospătați datele din cache?"
|
||||
},
|
||||
@@ -2649,12 +2649,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Setări de export",
|
||||
"description": "Descărcați setările în format JSON",
|
||||
"description": "Descărcați o copie a setărilor",
|
||||
"button": "Export"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Importați setările",
|
||||
"description": "Încărcați setările dintr-un fișier JSON",
|
||||
"description": "Restaurați setările dintr-un fișier",
|
||||
"button": "Import"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Offline",
|
||||
"privacy": "Confidențialitate",
|
||||
"about": "Despre"
|
||||
},
|
||||
"search": {
|
||||
"signature": "semnătură semnătură e-mail subsol",
|
||||
"sign_out": "deconectare ieșire logout",
|
||||
"app_lock": "PIN blocare cod ecran de blocare",
|
||||
"text_size": "mărime font text mai mare zoom mărire",
|
||||
"vacation": "răspuns automat în afara biroului absent concediu vacanță autoreply",
|
||||
"two_factor": "2FA doi factori doi pași MFA OTP autentificator cod unic",
|
||||
"encryption": "S/MIME smime PGP OpenPGP criptare certificat",
|
||||
"password": "parolă schimbă parola parolă nouă",
|
||||
"offline": "offline fără conexiune fără internet descărcare stocare sincronizare",
|
||||
"wipe": "elimină șterge golește dispozitiv date",
|
||||
"outbox": "căsuța de ieșire netrimis în așteptare coadă",
|
||||
"remote_content": "imagini la distanță conținut extern urmărire pixeli expeditori de încredere",
|
||||
"push": "push releu fundal notificări",
|
||||
"version": "versiune actualizare despre cod sursă GitHub licență"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Notificări",
|
||||
"push_link": {
|
||||
"label": "Releu pentru notificări",
|
||||
"description": "Notificările cu aplicația închisă trec printr-un releu push, care nu vă vede niciodată e-mailurile. Îl alegeți pe pagina Notificări.",
|
||||
"button": "Notificări"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Imagini la distanță și expeditori de încredere",
|
||||
"receipts_label": "Confirmări de citire",
|
||||
"senders_label": "Imaginile expeditorilor",
|
||||
"description": "Aceste setări sunt pe pagina Confidențialitate.",
|
||||
"button": "Confidențialitate"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Densitate și etichetele barei",
|
||||
"description": "Cât de apropiate sunt rândurile listei și dacă butoanele își arată numele se setează pe pagina Aspect."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Fundal deschis pentru mesaje",
|
||||
"description": "Afișarea mesajelor pe fundal deschis în modul întunecat se setează pe pagina Citire."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Previzualizarea evenimentelor în calendar",
|
||||
"description": "Dacă indicarea unui eveniment îi arată detaliile se setează pe pagina Calendar."
|
||||
},
|
||||
"list": {
|
||||
"label": "Conversații și rânduri de previzualizare",
|
||||
"description": "Gruparea pe conversații și rândul de previzualizare de sub fiecare mesaj se setează pe pagina Lista de mesaje."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Marcare ca citit",
|
||||
"description": "Când un mesaj contează ca citit și unde ajungeți după ce îl ștergeți sau îl mutați se setează pe pagina Citire."
|
||||
},
|
||||
"send_warnings_description": "Anularea trimiterii și avertismentele pentru subiect lipsă, atașament uitat sau text substituent.",
|
||||
"bulk_delete": {
|
||||
"label": "Întrebare înainte de a șterge mai multe mesaje",
|
||||
"description": "Aceasta se setează pe pagina Lista de mesaje."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Sunete și numărul de necitite",
|
||||
"description": "Sunetele notificărilor și numărul de necitite din filă se setează pe pagina Notificări."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Despre {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Deconectați acest cont pe acest dispozitiv."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Configurarea Bulwark Webmail",
|
||||
"subtitle": "Configurați instanța de webmail din browser.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# mesaj netrimis se pierde.} few {# mesaje netrimise se pierd.} other {# de mesaje netrimise se pierd.}}",
|
||||
"clear_confirm": "Golește",
|
||||
"persist_refused": "Acest browser poate șterge e-mailurile păstrate când se termină spațiul. Instalarea aplicației de obicei previne asta.",
|
||||
"persist_retry": "Întreabă din nou",
|
||||
"persist_retry": "Cereți browserului să le păstreze",
|
||||
"safari_hint": "Safari șterge datele unui site după 7 zile fără vizite. Adăugați Bulwark pe ecranul principal sau în Dock ca să păstrați e-mailurile pe acest dispozitiv.",
|
||||
"evicted": "Browserul a șters e-mailurile păstrate pe acest dispozitiv. Se descarcă din nou.",
|
||||
"evicted_lost": "{count, plural, one {# mesaj sau modificare netrimis s-a pierdut.} few {# mesaje sau modificări netrimise s-au pierdut.} other {# de mesaje sau modificări netrimise s-au pierdut.}}",
|
||||
|
||||
+80
-5
@@ -1976,7 +1976,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Автоматически выбирать адрес для ответа",
|
||||
"description": "При ответе автоматически переключать адрес отправителя на ту учетную запись, которая получила исходное сообщение"
|
||||
"description": "При ответе отправлять с адреса, на который пришло письмо."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Сопоставление адреса получения",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "Заново загрузить контакты, календари и папки с сервера. Вы останетесь в системе.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "Обновить кэшированные данные?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Экспортировать настройки",
|
||||
"description": "Скачать настройки в формате JSON",
|
||||
"description": "Скачать копию настроек",
|
||||
"button": "Экспортировать"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Импортировать настройки",
|
||||
"description": "Загрузить настройки из файла JSON",
|
||||
"description": "Восстановить настройки из файла",
|
||||
"button": "Импортировать"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Офлайн",
|
||||
"privacy": "Конфиденциальность",
|
||||
"about": "О программе"
|
||||
},
|
||||
"search": {
|
||||
"signature": "подпись подпись письма нижний колонтитул",
|
||||
"sign_out": "выйти выход logout разлогиниться",
|
||||
"app_lock": "PIN блокировка код экран блокировки",
|
||||
"text_size": "размер шрифта крупный текст масштаб увеличить",
|
||||
"vacation": "автоответ автоответчик нет на месте отпуск отсутствие autoreply",
|
||||
"two_factor": "2FA двухфакторная двухэтапная MFA OTP аутентификатор одноразовый код",
|
||||
"encryption": "S/MIME smime PGP OpenPGP шифрование сертификат",
|
||||
"password": "пароль сменить пароль новый пароль",
|
||||
"offline": "офлайн без сети без интернета скачать хранилище синхронизация",
|
||||
"wipe": "удалить стереть очистить устройство данные",
|
||||
"outbox": "исходящие неотправленные ожидание очередь",
|
||||
"remote_content": "внешние изображения внешний контент отслеживание пиксели доверенные отправители",
|
||||
"push": "push ретранслятор фоновые уведомления",
|
||||
"version": "версия обновление о программе исходный код GitHub лицензия"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Уведомления",
|
||||
"push_link": {
|
||||
"label": "Ретранслятор уведомлений",
|
||||
"description": "Уведомления при закрытом приложении идут через push-ретранслятор, который никогда не видит вашу почту. Он выбирается на странице «Уведомления».",
|
||||
"button": "Уведомления"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Внешние изображения и доверенные отправители",
|
||||
"receipts_label": "Уведомления о прочтении",
|
||||
"senders_label": "Изображения отправителей",
|
||||
"description": "Эти настройки находятся на странице «Конфиденциальность».",
|
||||
"button": "Конфиденциальность"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Плотность и подписи кнопок",
|
||||
"description": "Насколько плотно идут строки списка и показывают ли кнопки названия, настраивается на странице «Оформление»."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Светлый фон для писем",
|
||||
"description": "Показ писем на светлом фоне в тёмном режиме настраивается на странице «Чтение»."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Предпросмотр событий в календаре",
|
||||
"description": "Показывать ли подробности при наведении на событие, настраивается на странице «Календарь»."
|
||||
},
|
||||
"list": {
|
||||
"label": "Цепочки и строки предпросмотра",
|
||||
"description": "Группировка по цепочкам и строка предпросмотра под каждым письмом настраиваются на странице «Список писем»."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Отметка о прочтении",
|
||||
"description": "Когда письмо считается прочитанным и куда вы попадаете после удаления или перемещения, настраивается на странице «Чтение»."
|
||||
},
|
||||
"send_warnings_description": "Отмена отправки и предупреждения о пустой теме, забытом вложении или тексте-заполнителе.",
|
||||
"bulk_delete": {
|
||||
"label": "Спрашивать перед удалением нескольких писем",
|
||||
"description": "Это настраивается на странице «Список писем»."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Звуки и счётчик непрочитанных",
|
||||
"description": "Звуки уведомлений и число непрочитанных на вкладке настраиваются на странице «Уведомления»."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "О программе {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Выйти из этой учётной записи на этом устройстве."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Настройка Bulwark Webmail",
|
||||
"subtitle": "Настройте свой экземпляр веб-почты в браузере.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# неотправленное письмо будет потеряно.} few {# неотправленных письма будут потеряны.} many {# неотправленных писем будут потеряны.} other {# неотправленного письма будет потеряно.}}",
|
||||
"clear_confirm": "Очистить",
|
||||
"persist_refused": "Этот браузер может удалить сохранённую почту, когда не хватает места. Установка приложения обычно это предотвращает.",
|
||||
"persist_retry": "Спросить снова",
|
||||
"persist_retry": "Попросить браузер сохранить их",
|
||||
"safari_hint": "Safari удаляет данные сайта, если его не открывали 7 дней. Добавьте Bulwark на экран «Домой» или в Dock, чтобы почта оставалась на этом устройстве.",
|
||||
"evicted": "Браузер удалил почту, сохранённую на этом устройстве. Она загружается заново.",
|
||||
"evicted_lost": "{count, plural, one {Потеряно # неотправленное письмо или изменение.} few {Потеряно # неотправленных письма или изменения.} many {Потеряно # неотправленных писем или изменений.} other {Потеряно # неотправленного письма или изменения.}}",
|
||||
|
||||
+80
-5
@@ -1988,7 +1988,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Odpovedať z prijatej adresy",
|
||||
"description": "Pri odpovedaní automaticky prepnúť adresu odosielateľa na identitu, ktorá pôvodne prijala správu"
|
||||
"description": "Pri odpovedaní odoslať z adresy, na ktorú bola správa poslaná."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Porovnávanie prijímacej adresy",
|
||||
@@ -2636,7 +2636,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Obnoviť uložené údaje",
|
||||
"description": "Znova načíta kontakty, kalendáre a priečinky zo servera. Zachová vaše účty a prihlásenia — opraví zastarané alebo nesprávne zobrazenie bez odhlásenia.",
|
||||
"description": "Znova načítať kontakty, kalendáre a priečinky zo servera. Zostanete prihlásení.",
|
||||
"button": "Obnoviť",
|
||||
"confirm_title": "Obnoviť údaje vo vyrovnávacej pamäti?"
|
||||
},
|
||||
@@ -2649,12 +2649,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Exportovať nastavenia",
|
||||
"description": "Stiahnuť svoje nastavenia ako súbor JSON",
|
||||
"description": "Stiahnuť kópiu nastavení",
|
||||
"button": "Exportovať"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Importovať nastavenia",
|
||||
"description": "Nahrať nastavenia zo súboru JSON",
|
||||
"description": "Obnoviť nastavenia zo súboru",
|
||||
"button": "Importovať"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Offline",
|
||||
"privacy": "Súkromie",
|
||||
"about": "O aplikácii"
|
||||
},
|
||||
"search": {
|
||||
"signature": "podpis e-mailový podpis päta",
|
||||
"sign_out": "odhlásiť odhlásenie logout",
|
||||
"app_lock": "PIN zámok kód uzamknúť obrazovka",
|
||||
"text_size": "veľkosť písma väčší text zoom zväčšiť",
|
||||
"vacation": "automatická odpoveď mimo kancelárie neprítomnosť dovolenka autoreply",
|
||||
"two_factor": "2FA dvojfázové dvojfaktorové MFA OTP autentifikátor jednorazový kód",
|
||||
"encryption": "S/MIME smime PGP OpenPGP šifrovanie certifikát",
|
||||
"password": "heslo zmeniť heslo nové heslo",
|
||||
"offline": "offline bez pripojenia bez internetu stiahnuť úložisko synchronizácia",
|
||||
"wipe": "odstrániť zmazať vymazať zariadenie údaje",
|
||||
"outbox": "na odoslanie neodoslané čakajúce front",
|
||||
"remote_content": "vzdialené obrázky externý obsah sledovanie pixely dôveryhodní odosielatelia",
|
||||
"push": "push relay na pozadí upozornenia",
|
||||
"version": "verzia aktualizácia o aplikácii zdrojový kód GitHub licencia"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Upozornenia",
|
||||
"push_link": {
|
||||
"label": "Relay pre upozornenia",
|
||||
"description": "Upozornenia pri zatvorenej aplikácii idú cez push relay, ktorý nikdy nevidí vašu poštu. Vyberiete ho na stránke Upozornenia.",
|
||||
"button": "Upozornenia"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Vzdialené obrázky a dôveryhodní odosielatelia",
|
||||
"receipts_label": "Potvrdenia o prečítaní",
|
||||
"senders_label": "Obrázky odosielateľov",
|
||||
"description": "Tieto nastavenia nájdete na stránke Súkromie.",
|
||||
"button": "Súkromie"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Hustota a popisy panela",
|
||||
"description": "Ako tesne sú riadky zoznamu a či tlačidlá ukazujú svoj názov, nastavíte na stránke Vzhľad."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Svetlé pozadie správ",
|
||||
"description": "Zobrazenie správ na svetlom pozadí v tmavom režime nastavíte na stránke Čítanie."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Náhľad udalostí v kalendári",
|
||||
"description": "Či ukázanie na udalosť zobrazí jej podrobnosti, nastavíte na stránke Kalendár."
|
||||
},
|
||||
"list": {
|
||||
"label": "Konverzácie a riadky náhľadu",
|
||||
"description": "Zoskupenie podľa konverzácie a riadok náhľadu pod každou správou nastavíte na stránke Zoznam správ."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Označenie ako prečítané",
|
||||
"description": "Kedy sa správa počíta ako prečítaná a kam sa dostanete po zmazaní alebo presune, nastavíte na stránke Čítanie."
|
||||
},
|
||||
"send_warnings_description": "Zrušenie odoslania a upozornenia na chýbajúci predmet, zabudnutú prílohu alebo zástupný text.",
|
||||
"bulk_delete": {
|
||||
"label": "Opýtať sa pred zmazaním viacerých správ",
|
||||
"description": "To nastavíte na stránke Zoznam správ."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Zvuky a počet neprečítaných",
|
||||
"description": "Zvuky upozornení a počet neprečítaných na karte nastavíte na stránke Upozornenia."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "O aplikácii {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Odhlásiť tento účet na tomto zariadení."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Nastavenie Bulwark Webmail",
|
||||
"subtitle": "Nastavte svoju inštanciu webmailu v prehliadači.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# neodoslaná správa sa stratí.} few {# neodoslané správy sa stratia.} many {# neodoslanej správy sa stratí.} other {# neodoslaných správ sa stratí.}}",
|
||||
"clear_confirm": "Vymazať",
|
||||
"persist_refused": "Tento prehliadač môže uchovanú poštu zmazať, keď dôjde miesto. Inštalácia aplikácie tomu zvyčajne zabráni.",
|
||||
"persist_retry": "Opýtať sa znova",
|
||||
"persist_retry": "Požiadať prehliadač o zachovanie",
|
||||
"safari_hint": "Safari zmaže dáta webu po 7 dňoch bez návštevy. Pridajte Bulwark na plochu alebo do Docku, aby vaša pošta na tomto zariadení zostala.",
|
||||
"evicted": "Prehliadač zmazal poštu uchovanú na tomto zariadení. Sťahuje sa znova.",
|
||||
"evicted_lost": "{count, plural, one {# neodoslaná správa alebo zmena sa stratila.} few {# neodoslané správy alebo zmeny sa stratili.} many {# neodoslanej správy alebo zmeny sa stratilo.} other {# neodoslaných správ alebo zmien sa stratilo.}}",
|
||||
|
||||
+80
-5
@@ -2233,7 +2233,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, other {ข้อความที่ยังไม่ได้ส่ง # ข้อความจะสูญหาย}}",
|
||||
"clear_confirm": "ล้าง",
|
||||
"persist_refused": "เบราว์เซอร์นี้อาจลบอีเมลที่เก็บไว้เมื่อพื้นที่เหลือน้อย การติดตั้งแอปมักช่วยป้องกันได้",
|
||||
"persist_retry": "ขออีกครั้ง",
|
||||
"persist_retry": "ขอให้เบราว์เซอร์เก็บไว้",
|
||||
"safari_hint": "Safari จะลบข้อมูลของเว็บไซต์หลังจากไม่ได้เข้าชม 7 วัน เพิ่ม Bulwark ลงในหน้าจอโฮมหรือ Dock เพื่อเก็บอีเมลไว้บนอุปกรณ์นี้",
|
||||
"evicted": "เบราว์เซอร์ลบอีเมลที่เก็บไว้บนอุปกรณ์นี้แล้ว กำลังดาวน์โหลดใหม่",
|
||||
"evicted_lost": "{count, plural, other {ข้อความหรือการเปลี่ยนแปลงที่ยังไม่ได้ส่ง # รายการสูญหาย}}",
|
||||
@@ -4892,7 +4892,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "ตอบกลับจากที่อยู่ที่ได้รับ",
|
||||
"description": "เมื่อตอบกลับ ให้ส่งจากที่อยู่อีเมลที่ข้อความนั้นถูกส่งมาถึงในตอนแรก ข้อมูลประจำตัวที่คุณตั้งค่าไว้จะตรงกันเสมอ ส่วนที่อยู่อื่นๆ บนโดเมนของคุณจะนับรวมด้วยหรือไม่ (catch-all) จะถูกกำหนดโดย “การจับคู่ที่อยู่ที่ได้รับ” ด้านล่าง"
|
||||
"description": "เมื่อตอบกลับ ให้ส่งจากที่อยู่ที่ข้อความถูกส่งมาถึง"
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "การจับคู่ที่อยู่ที่ได้รับ",
|
||||
@@ -5540,7 +5540,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "รีเฟรชข้อมูลในแคช",
|
||||
"description": "โหลดผู้ติดต่อ ปฏิทิน และโฟลเดอร์จากเซิร์ฟเวอร์ใหม่ โดยยังคงรักษาบัญชีและเซสชันของคุณไว้ — แก้ไขมุมมองที่ไม่อัปเดตหรือไม่ถูกต้องโดยไม่ต้องลงชื่อออก",
|
||||
"description": "โหลดผู้ติดต่อ ปฏิทิน และโฟลเดอร์จากเซิร์ฟเวอร์ใหม่ คุณยังคงลงชื่อเข้าใช้อยู่",
|
||||
"button": "รีเฟรช",
|
||||
"confirm_title": "รีเฟรชข้อมูลที่แคชไว้ไหม"
|
||||
},
|
||||
@@ -5553,12 +5553,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "ส่งออกการตั้งค่า",
|
||||
"description": "ดาวน์โหลดการตั้งค่าของคุณเป็นไฟล์ JSON",
|
||||
"description": "ดาวน์โหลดสำเนาการตั้งค่า",
|
||||
"button": "ส่งออก"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "นำเข้าการตั้งค่า",
|
||||
"description": "อัปโหลดการตั้งค่าจากไฟล์ JSON",
|
||||
"description": "กู้คืนการตั้งค่าจากไฟล์",
|
||||
"button": "นำเข้า"
|
||||
},
|
||||
"about": {
|
||||
@@ -6292,6 +6292,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "ออฟไลน์",
|
||||
"privacy": "ความเป็นส่วนตัว",
|
||||
"about": "เกี่ยวกับ"
|
||||
},
|
||||
"search": {
|
||||
"signature": "ลายเซ็น ลายเซ็นอีเมล ส่วนท้าย",
|
||||
"sign_out": "ออกจากระบบ ลงชื่อออก logout",
|
||||
"app_lock": "PIN ล็อก รหัส หน้าจอล็อก",
|
||||
"text_size": "ขนาดตัวอักษร ตัวอักษรใหญ่ ซูม ขยาย",
|
||||
"vacation": "ตอบกลับอัตโนมัติ ไม่อยู่ที่สำนักงาน ไม่อยู่ ลาพักร้อน วันหยุด autoreply",
|
||||
"two_factor": "2FA สองปัจจัย สองขั้นตอน MFA OTP แอปยืนยันตัวตน รหัสใช้ครั้งเดียว",
|
||||
"encryption": "S/MIME smime PGP OpenPGP การเข้ารหัส ใบรับรอง",
|
||||
"password": "รหัสผ่าน เปลี่ยนรหัสผ่าน รหัสผ่านใหม่",
|
||||
"offline": "ออฟไลน์ offline ไม่มีการเชื่อมต่อ ไม่มีอินเทอร์เน็ต ดาวน์โหลด พื้นที่จัดเก็บ ซิงค์",
|
||||
"wipe": "ลบ ล้าง อุปกรณ์ ข้อมูล",
|
||||
"outbox": "กล่องขาออก ยังไม่ส่ง รอส่ง คิว",
|
||||
"remote_content": "รูปภาพภายนอก เนื้อหาภายนอก การติดตาม พิกเซล ผู้ส่งที่เชื่อถือ",
|
||||
"push": "พุช push รีเลย์ เบื้องหลัง การแจ้งเตือน",
|
||||
"version": "เวอร์ชัน อัปเดต เกี่ยวกับ ซอร์สโค้ด GitHub สัญญาอนุญาต"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "การแจ้งเตือน",
|
||||
"push_link": {
|
||||
"label": "รีเลย์การแจ้งเตือน",
|
||||
"description": "การแจ้งเตือนขณะปิดแอปจะผ่านรีเลย์พุช ซึ่งไม่เคยเห็นอีเมลของคุณ คุณเลือกได้ในหน้าการแจ้งเตือน",
|
||||
"button": "การแจ้งเตือน"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "รูปภาพภายนอกและผู้ส่งที่เชื่อถือ",
|
||||
"receipts_label": "ใบตอบรับการอ่าน",
|
||||
"senders_label": "รูปภาพผู้ส่ง",
|
||||
"description": "การตั้งค่าเหล่านี้อยู่ในหน้าความเป็นส่วนตัว",
|
||||
"button": "ความเป็นส่วนตัว"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "ความหนาแน่นและป้ายปุ่ม",
|
||||
"description": "ระยะห่างของแถวในรายการและการแสดงชื่อบนปุ่ม ตั้งค่าได้ในหน้ารูปลักษณ์"
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "พื้นหลังสว่างสำหรับข้อความ",
|
||||
"description": "การแสดงข้อความบนพื้นหลังสว่างในโหมดมืด ตั้งค่าได้ในหน้าการอ่าน"
|
||||
},
|
||||
"calendar": {
|
||||
"label": "ตัวอย่างกิจกรรมในปฏิทิน",
|
||||
"description": "การแสดงรายละเอียดเมื่อชี้ที่กิจกรรม ตั้งค่าได้ในหน้าปฏิทิน"
|
||||
},
|
||||
"list": {
|
||||
"label": "การสนทนาและบรรทัดตัวอย่าง",
|
||||
"description": "การจัดกลุ่มตามการสนทนาและบรรทัดตัวอย่างใต้แต่ละข้อความ ตั้งค่าได้ในหน้ารายการข้อความ"
|
||||
},
|
||||
"reading": {
|
||||
"label": "ทำเครื่องหมายว่าอ่านแล้ว",
|
||||
"description": "เมื่อใดข้อความจะนับว่าอ่านแล้ว และจะไปที่ใดหลังลบหรือย้าย ตั้งค่าได้ในหน้าการอ่าน"
|
||||
},
|
||||
"send_warnings_description": "การยกเลิกการส่ง และคำเตือนเรื่องไม่มีหัวเรื่อง ลืมแนบไฟล์ หรือข้อความตัวยึดตำแหน่ง",
|
||||
"bulk_delete": {
|
||||
"label": "ถามก่อนลบหลายข้อความ",
|
||||
"description": "ตั้งค่าได้ในหน้ารายการข้อความ"
|
||||
},
|
||||
"sounds": {
|
||||
"label": "เสียงและตัวนับที่ยังไม่อ่าน",
|
||||
"description": "เสียงแจ้งเตือนและจำนวนที่ยังไม่อ่านบนแท็บ ตั้งค่าได้ในหน้าการแจ้งเตือน"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "เกี่ยวกับ {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "ออกจากระบบบัญชีนี้บนอุปกรณ์นี้"
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "ตั้งค่า Bulwark Webmail",
|
||||
"subtitle": "ตั้งค่าอินสแตนซ์เว็บเมลของคุณจากเบราว์เซอร์",
|
||||
|
||||
+80
-5
@@ -1985,7 +1985,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Yanıt Adresini Otomatik Seç",
|
||||
"description": "Yanıtlarken, Kimden adresini iletiyi başlangıçta alan kimliğe otomatik olarak değiştir"
|
||||
"description": "Yanıtlarken, iletinin gönderildiği adresten gönder."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Alıcı adresi eşleştirme",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "Kişileri, takvimleri ve klasörleri sunucudan yeniden yükleyin. Oturumunuz açık kalır.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "Önbellekteki veriler yenilensin mi?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Ayarları Dışa Aktar",
|
||||
"description": "Ayarlarınızı JSON olarak indirin",
|
||||
"description": "Ayarlarınızın bir kopyasını indirin",
|
||||
"button": "Dışa Aktar"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Ayarları İçe Aktar",
|
||||
"description": "JSON dosyasından ayarları yükle",
|
||||
"description": "Ayarları bir dosyadan geri yükleyin",
|
||||
"button": "İçe Aktar"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Çevrimdışı",
|
||||
"privacy": "Gizlilik",
|
||||
"about": "Hakkında"
|
||||
},
|
||||
"search": {
|
||||
"signature": "imza e-posta imzası alt bilgi",
|
||||
"sign_out": "oturumu kapat çıkış yap logout",
|
||||
"app_lock": "PIN kilit kod kilit ekranı",
|
||||
"text_size": "yazı tipi boyutu büyük metin yakınlaştır zoom",
|
||||
"vacation": "otomatik yanıt ofis dışında uzakta tatil izin autoreply",
|
||||
"two_factor": "2FA iki faktörlü iki adımlı MFA OTP doğrulayıcı tek kullanımlık kod",
|
||||
"encryption": "S/MIME smime PGP OpenPGP şifreleme sertifika",
|
||||
"password": "parola şifre parolayı değiştir yeni parola",
|
||||
"offline": "çevrimdışı bağlantısız internetsiz indir depolama eşitleme",
|
||||
"wipe": "kaldır sil temizle cihaz veriler",
|
||||
"outbox": "giden kutusu gönderilmemiş bekleyen kuyruk",
|
||||
"remote_content": "uzak resimler harici içerik izleme pikseller güvenilir gönderenler",
|
||||
"push": "push aktarıcı arka plan bildirimler",
|
||||
"version": "sürüm güncelleme hakkında kaynak kodu GitHub lisans"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Bildirimler",
|
||||
"push_link": {
|
||||
"label": "Bildirim aktarıcısı",
|
||||
"description": "Uygulama kapalıyken gelen bildirimler, e-postanızı asla görmeyen bir push aktarıcısından geçer. Bunu Bildirimler sayfasında seçersiniz.",
|
||||
"button": "Bildirimler"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Uzak resimler ve güvenilir gönderenler",
|
||||
"receipts_label": "Okundu bilgileri",
|
||||
"senders_label": "Gönderen resimleri",
|
||||
"description": "Bu ayarlar Gizlilik sayfasındadır.",
|
||||
"button": "Gizlilik"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Yoğunluk ve düğme etiketleri",
|
||||
"description": "Liste satırlarının ne kadar sık olduğu ve düğmelerin adını gösterip göstermediği Görünüm sayfasında ayarlanır."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "İletiler için açık arka plan",
|
||||
"description": "Koyu modda iletilerin açık arka planda gösterilmesi Okuma sayfasında ayarlanır."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Takvimde etkinlik önizlemesi",
|
||||
"description": "Bir etkinliğin üzerine gelince ayrıntılarının görünüp görünmeyeceği Takvim sayfasında ayarlanır."
|
||||
},
|
||||
"list": {
|
||||
"label": "Konuşmalar ve önizleme satırları",
|
||||
"description": "Konuşmaya göre gruplama ve her iletinin altındaki önizleme satırı İleti listesi sayfasında ayarlanır."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Okundu olarak işaretleme",
|
||||
"description": "Bir iletinin ne zaman okunmuş sayılacağı ve silip taşıdıktan sonra nereye gideceğiniz Okuma sayfasında ayarlanır."
|
||||
},
|
||||
"send_warnings_description": "Göndermeyi geri alma ve eksik konu, unutulan ek veya yer tutucu metin uyarıları.",
|
||||
"bulk_delete": {
|
||||
"label": "Birden çok iletiyi silmeden önce sor",
|
||||
"description": "Bu, İleti listesi sayfasında ayarlanır."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Sesler ve okunmamış sayacı",
|
||||
"description": "Bildirim sesleri ve sekmedeki okunmamış sayısı Bildirimler sayfasında ayarlanır."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "{appName} hakkında"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Bu cihazda bu hesabın oturumunu kapatın."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Bulwark Webmail kurulumu",
|
||||
"subtitle": "Webmail örneğinizi tarayıcıdan yapılandırın.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# gönderilmemiş ileti kaybolacak.} other {# gönderilmemiş ileti kaybolacak.}}",
|
||||
"clear_confirm": "Temizle",
|
||||
"persist_refused": "Alan azaldığında bu tarayıcı tutulan e-postaları silebilir. Uygulamayı yüklemek genellikle bunu önler.",
|
||||
"persist_retry": "Yeniden sor",
|
||||
"persist_retry": "Tarayıcıdan saklamasını iste",
|
||||
"safari_hint": "Safari, 7 gün ziyaret edilmeyen bir sitenin verilerini siler. E-postalarınızı bu cihazda tutmak için Bulwark'ı Ana Ekran'a veya Dock'a ekleyin.",
|
||||
"evicted": "Tarayıcı, bu cihazda tutulan e-postaları sildi. Yeniden indiriliyor.",
|
||||
"evicted_lost": "{count, plural, one {# gönderilmemiş ileti veya değişiklik kayboldu.} other {# gönderilmemiş ileti veya değişiklik kayboldu.}}",
|
||||
|
||||
+80
-5
@@ -1985,7 +1985,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Автоматичний вибір адреси для відповіді",
|
||||
"description": "Під час відповіді автоматично змінюйте адресу відправника на особу, яка спочатку отримала повідомлення"
|
||||
"description": "Відповідаючи, надсилати з адреси, на яку прийшов лист."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Зіставлення адреси отримання",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "Знову завантажити контакти, календарі й теки з сервера. Ви залишаєтеся в системі.",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "Оновити кешовані дані?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Налаштування експорту",
|
||||
"description": "Завантажте налаштування як JSON",
|
||||
"description": "Завантажити копію налаштувань",
|
||||
"button": "Експорт"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Налаштування імпорту",
|
||||
"description": "Завантажте налаштування з файлу JSON",
|
||||
"description": "Відновити налаштування з файлу",
|
||||
"button": "Імпорт"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Офлайн",
|
||||
"privacy": "Конфіденційність",
|
||||
"about": "Про програму"
|
||||
},
|
||||
"search": {
|
||||
"signature": "підпис підпис листа нижній колонтитул",
|
||||
"sign_out": "вийти вихід logout",
|
||||
"app_lock": "PIN блокування код екран блокування",
|
||||
"text_size": "розмір шрифту більший текст масштаб збільшити",
|
||||
"vacation": "автовідповідь немає на місці відпустка відсутність autoreply",
|
||||
"two_factor": "2FA двофакторна двоетапна MFA OTP автентифікатор одноразовий код",
|
||||
"encryption": "S/MIME smime PGP OpenPGP шифрування сертифікат",
|
||||
"password": "пароль змінити пароль новий пароль",
|
||||
"offline": "офлайн без мережі без інтернету завантажити сховище синхронізація",
|
||||
"wipe": "видалити стерти очистити пристрій дані",
|
||||
"outbox": "вихідні невідправлені очікування черга",
|
||||
"remote_content": "зовнішні зображення зовнішній вміст відстеження пікселі довірені відправники",
|
||||
"push": "push ретранслятор фонові сповіщення",
|
||||
"version": "версія оновлення про програму вихідний код GitHub ліцензія"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Сповіщення",
|
||||
"push_link": {
|
||||
"label": "Ретранслятор сповіщень",
|
||||
"description": "Сповіщення при закритому застосунку йдуть через push-ретранслятор, який ніколи не бачить вашої пошти. Його можна вибрати на сторінці «Сповіщення».",
|
||||
"button": "Сповіщення"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Зовнішні зображення і довірені відправники",
|
||||
"receipts_label": "Сповіщення про прочитання",
|
||||
"senders_label": "Зображення відправників",
|
||||
"description": "Ці налаштування на сторінці «Конфіденційність».",
|
||||
"button": "Конфіденційність"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Щільність і підписи кнопок",
|
||||
"description": "Наскільки щільно йдуть рядки списку і чи показують кнопки назви, налаштовується на сторінці «Вигляд»."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Світле тло для листів",
|
||||
"description": "Показ листів на світлому тлі в темному режимі налаштовується на сторінці «Читання»."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Попередній перегляд подій у календарі",
|
||||
"description": "Чи показувати подробиці при наведенні на подію, налаштовується на сторінці «Календар»."
|
||||
},
|
||||
"list": {
|
||||
"label": "Ланцюжки і рядки попереднього перегляду",
|
||||
"description": "Групування за ланцюжками і рядок попереднього перегляду під кожним листом налаштовуються на сторінці «Список листів»."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Позначення прочитаним",
|
||||
"description": "Коли лист вважається прочитаним і куди ви потрапляєте після видалення чи переміщення, налаштовується на сторінці «Читання»."
|
||||
},
|
||||
"send_warnings_description": "Скасування надсилання і попередження про порожню тему, забуте вкладення чи текст-заповнювач.",
|
||||
"bulk_delete": {
|
||||
"label": "Питати перед видаленням кількох листів",
|
||||
"description": "Це налаштовується на сторінці «Список листів»."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Звуки й лічильник непрочитаних",
|
||||
"description": "Звуки сповіщень і кількість непрочитаних на вкладці налаштовуються на сторінці «Сповіщення»."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Про {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Вийти з цього облікового запису на цьому пристрої."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Налаштування Bulwark Webmail",
|
||||
"subtitle": "Налаштуйте свій екземпляр вебпошти в браузері.",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, one {# невідправлений лист буде втрачено.} few {# невідправлені листи буде втрачено.} many {# невідправлених листів буде втрачено.} other {# невідправленого листа буде втрачено.}}",
|
||||
"clear_confirm": "Очистити",
|
||||
"persist_refused": "Цей браузер може видалити збережену пошту, коли бракує місця. Встановлення застосунку зазвичай цьому запобігає.",
|
||||
"persist_retry": "Запитати знову",
|
||||
"persist_retry": "Попросити браузер зберегти їх",
|
||||
"safari_hint": "Safari видаляє дані сайту, якщо його не відвідували 7 днів. Додайте Bulwark на екран «Додому» або в Dock, щоб пошта залишалася на цьому пристрої.",
|
||||
"evicted": "Браузер видалив пошту, збережену на цьому пристрої. Вона завантажується знову.",
|
||||
"evicted_lost": "{count, plural, one {Втрачено # невідправлений лист або зміну.} few {Втрачено # невідправлені листи або зміни.} many {Втрачено # невідправлених листів або змін.} other {Втрачено # невідправленого листа або зміни.}}",
|
||||
|
||||
+80
-5
@@ -2000,7 +2000,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "Trả lời bằng địa chỉ nhận thư",
|
||||
"description": "Khi trả lời, gửi thư từ địa chỉ mà thư ban đầu được gửi đến. Các danh tính bạn đã cấu hình luôn được khớp; tùy chọn “Khớp địa chỉ nhận” bên dưới quyết định có tính các địa chỉ khác trong miền của bạn (catch-all) hay không."
|
||||
"description": "Khi trả lời, gửi từ địa chỉ mà thư đã được gửi đến."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Khớp địa chỉ nhận",
|
||||
@@ -2648,7 +2648,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Làm mới dữ liệu được lưu trong bộ nhớ đệm",
|
||||
"description": "Tải lại danh bạ, lịch và thư mục từ máy chủ. Giữ lại các tài khoản và phiên của bạn — sửa chế độ xem cũ hoặc sai mà không cần đăng xuất.",
|
||||
"description": "Tải lại danh bạ, lịch và thư mục từ máy chủ. Bạn vẫn đăng nhập.",
|
||||
"button": "Làm mới",
|
||||
"confirm_title": "Làm mới dữ liệu đã lưu vào bộ nhớ đệm?"
|
||||
},
|
||||
@@ -2661,12 +2661,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "Xuất cài đặt",
|
||||
"description": "Tải xuống cài đặt của bạn dưới dạng JSON",
|
||||
"description": "Tải xuống bản sao cài đặt",
|
||||
"button": "Xuất"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "Nhập cài đặt",
|
||||
"description": "Tải lên cài đặt từ tệp JSON",
|
||||
"description": "Khôi phục cài đặt từ tệp",
|
||||
"button": "Nhập"
|
||||
},
|
||||
"about": {
|
||||
@@ -3400,6 +3400,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "Ngoại tuyến",
|
||||
"privacy": "Quyền riêng tư",
|
||||
"about": "Giới thiệu"
|
||||
},
|
||||
"search": {
|
||||
"signature": "chữ ký chữ ký email chân thư",
|
||||
"sign_out": "đăng xuất thoát logout",
|
||||
"app_lock": "PIN khóa mã màn hình khóa",
|
||||
"text_size": "cỡ chữ chữ to hơn phóng to thu phóng",
|
||||
"vacation": "trả lời tự động vắng mặt nghỉ phép kỳ nghỉ ngoài văn phòng autoreply",
|
||||
"two_factor": "2FA hai lớp hai bước MFA OTP ứng dụng xác thực mã dùng một lần",
|
||||
"encryption": "S/MIME smime PGP OpenPGP mã hóa chứng chỉ",
|
||||
"password": "mật khẩu đổi mật khẩu mật khẩu mới",
|
||||
"offline": "ngoại tuyến offline không có kết nối không có mạng tải xuống bộ nhớ đồng bộ",
|
||||
"wipe": "xóa gỡ dọn thiết bị dữ liệu",
|
||||
"outbox": "hộp thư đi chưa gửi đang chờ hàng đợi",
|
||||
"remote_content": "hình ảnh từ xa nội dung bên ngoài theo dõi pixel người gửi tin cậy",
|
||||
"push": "đẩy push chuyển tiếp nền thông báo",
|
||||
"version": "phiên bản cập nhật giới thiệu mã nguồn GitHub giấy phép"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "Thông báo",
|
||||
"push_link": {
|
||||
"label": "Máy chuyển tiếp thông báo",
|
||||
"description": "Thông báo khi ứng dụng đóng đi qua một máy chuyển tiếp đẩy, máy này không bao giờ thấy thư của bạn. Bạn chọn nó trên trang Thông báo.",
|
||||
"button": "Thông báo"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "Hình ảnh từ xa và người gửi tin cậy",
|
||||
"receipts_label": "Xác nhận đã đọc",
|
||||
"senders_label": "Ảnh người gửi",
|
||||
"description": "Các cài đặt này nằm trên trang Quyền riêng tư.",
|
||||
"button": "Quyền riêng tư"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "Mật độ và nhãn nút",
|
||||
"description": "Khoảng cách các dòng trong danh sách và việc nút có hiện tên hay không được đặt trên trang Giao diện."
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "Nền sáng cho thư",
|
||||
"description": "Hiển thị thư trên nền sáng ở chế độ tối được đặt trên trang Đọc thư."
|
||||
},
|
||||
"calendar": {
|
||||
"label": "Xem trước sự kiện trong lịch",
|
||||
"description": "Việc trỏ vào sự kiện có hiện chi tiết hay không được đặt trên trang Lịch."
|
||||
},
|
||||
"list": {
|
||||
"label": "Cuộc trò chuyện và dòng xem trước",
|
||||
"description": "Nhóm theo cuộc trò chuyện và dòng xem trước dưới mỗi thư được đặt trên trang Danh sách thư."
|
||||
},
|
||||
"reading": {
|
||||
"label": "Đánh dấu đã đọc",
|
||||
"description": "Khi nào thư được tính là đã đọc, và bạn đi đâu sau khi xóa hoặc di chuyển thư, được đặt trên trang Đọc thư."
|
||||
},
|
||||
"send_warnings_description": "Hoàn tác gửi và các cảnh báo khi thiếu tiêu đề, quên tệp đính kèm hoặc còn văn bản giữ chỗ.",
|
||||
"bulk_delete": {
|
||||
"label": "Hỏi trước khi xóa nhiều thư",
|
||||
"description": "Mục này được đặt trên trang Danh sách thư."
|
||||
},
|
||||
"sounds": {
|
||||
"label": "Âm thanh và số thư chưa đọc",
|
||||
"description": "Âm thanh thông báo và số thư chưa đọc trên thẻ được đặt trên trang Thông báo."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Giới thiệu {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "Đăng xuất tài khoản này trên thiết bị này."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Thiết lập webmail Bulwark",
|
||||
"subtitle": "Định cấu hình phiên bản webmail của bạn từ trình duyệt.",
|
||||
@@ -5906,7 +5981,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, other {# thư chưa gửi sẽ bị mất.}}",
|
||||
"clear_confirm": "Xóa",
|
||||
"persist_refused": "Trình duyệt này có thể xóa thư đã giữ khi sắp hết dung lượng. Cài đặt ứng dụng thường giúp tránh điều này.",
|
||||
"persist_retry": "Hỏi lại",
|
||||
"persist_retry": "Đề nghị trình duyệt giữ lại",
|
||||
"safari_hint": "Safari xóa dữ liệu của một trang web sau 7 ngày không truy cập. Hãy thêm Bulwark vào Màn hình chính hoặc Dock để giữ thư trên thiết bị này.",
|
||||
"evicted": "Trình duyệt đã xóa thư được giữ trên thiết bị này. Đang tải lại.",
|
||||
"evicted_lost": "{count, plural, other {# thư hoặc thay đổi chưa gửi đã bị mất.}}",
|
||||
|
||||
@@ -312,7 +312,7 @@
|
||||
"description": "Soạn tất cả thư dưới dạng văn bản thuần, không có chữ đậm, màu sắc hoặc hình ảnh. Áp dụng cả cho thư trả lời và thư chuyển tiếp."
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"description": "Khi trả lời, thư sẽ được gửi từ địa chỉ đã nhận thư gốc. Áp dụng cho tất cả địa chỉ riêng của bạn. Bên dưới, bạn có thể chọn xem có tính cả các địa chỉ khác trên miền của mình hay không."
|
||||
"description": "Khi trả lời, thư sẽ được gửi từ địa chỉ đã nhận thư gốc. Áp dụng cho tất cả địa chỉ riêng của bạn."
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "Những địa chỉ được tính là của bạn",
|
||||
@@ -577,10 +577,10 @@
|
||||
"description": "Đưa tất cả cài đặt về trạng thái ban đầu"
|
||||
},
|
||||
"export_settings": {
|
||||
"description": "Lưu cài đặt thành tệp trên thiết bị"
|
||||
"description": "Tải xuống bản sao cài đặt"
|
||||
},
|
||||
"import_settings": {
|
||||
"description": "Tải cài đặt từ tệp bạn đã lưu trước đó"
|
||||
"description": "Khôi phục cài đặt từ tệp"
|
||||
},
|
||||
"sections": {
|
||||
"backup_description": "Dùng cùng cài đặt trên các trình duyệt và thiết bị khác.",
|
||||
|
||||
@@ -1988,7 +1988,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "使用收件地址回覆",
|
||||
"description": "回覆時,使用原始郵件的收件地址寄送。系統會先比對寄件身分;若是網域 catch-all 收件,則透過主要寄件身分傳送,但將 From 標頭改寫為別名地址。"
|
||||
"description": "回覆時,從郵件寄達的地址寄出。"
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "接收地址比對",
|
||||
@@ -2636,7 +2636,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "重新整理快取資料",
|
||||
"description": "從伺服器重新載入聯絡人、行事曆與信件匣。保留帳號與工作階段,不需登出即可修正過時或錯誤的畫面。",
|
||||
"description": "從伺服器重新載入聯絡人、行事曆與信件匣。你會保持登入。",
|
||||
"button": "重新整理",
|
||||
"confirm_title": "重新整理快取資料?"
|
||||
},
|
||||
@@ -2649,12 +2649,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "匯出設定",
|
||||
"description": "將設定匯出為 JSON 檔案",
|
||||
"description": "下載設定的副本",
|
||||
"button": "匯出"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "匯入設定",
|
||||
"description": "從 JSON 檔案匯入設定",
|
||||
"description": "從檔案還原設定",
|
||||
"button": "匯入"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "離線",
|
||||
"privacy": "隱私",
|
||||
"about": "關於"
|
||||
},
|
||||
"search": {
|
||||
"signature": "簽名 郵件簽名 頁尾",
|
||||
"sign_out": "登出 退出 logout",
|
||||
"app_lock": "PIN 鎖定 密碼鎖 鎖定畫面",
|
||||
"text_size": "字型大小 字級 大字 放大 縮放",
|
||||
"vacation": "自動回覆 不在辦公室 外出 休假 假期 autoreply",
|
||||
"two_factor": "2FA 雙重驗證 兩步驟驗證 MFA OTP 驗證器 一次性驗證碼",
|
||||
"encryption": "S/MIME smime PGP OpenPGP 加密 憑證",
|
||||
"password": "密碼 變更密碼 新密碼",
|
||||
"offline": "離線 offline 無網路 無連線 下載 儲存空間 同步",
|
||||
"wipe": "移除 刪除 清除 裝置 資料",
|
||||
"outbox": "寄件匣 未寄出 等待中 佇列",
|
||||
"remote_content": "遠端圖片 外部內容 追蹤 像素 信任的寄件者",
|
||||
"push": "推播 push 中繼 背景 通知",
|
||||
"version": "版本 更新 關於 原始碼 GitHub 授權"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "通知",
|
||||
"push_link": {
|
||||
"label": "通知中繼",
|
||||
"description": "應用程式關閉時的通知會經過推播中繼,中繼永遠看不到你的郵件。可在「通知」頁面選擇。",
|
||||
"button": "通知"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "遠端圖片和信任的寄件者",
|
||||
"receipts_label": "讀取回條",
|
||||
"senders_label": "寄件者圖片",
|
||||
"description": "這些設定在「隱私」頁面。",
|
||||
"button": "隱私"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "密度和按鈕標籤",
|
||||
"description": "清單列的疏密以及按鈕是否顯示名稱,在「外觀」頁面設定。"
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "郵件使用淺色背景",
|
||||
"description": "深色模式下郵件是否使用淺色背景,在「閱讀」頁面設定。"
|
||||
},
|
||||
"calendar": {
|
||||
"label": "行事曆中的活動預覽",
|
||||
"description": "指向活動時是否顯示詳細資料,在「行事曆」頁面設定。"
|
||||
},
|
||||
"list": {
|
||||
"label": "對話和預覽列",
|
||||
"description": "依對話分組以及每封郵件下方的預覽列,在「郵件清單」頁面設定。"
|
||||
},
|
||||
"reading": {
|
||||
"label": "標示為已讀",
|
||||
"description": "郵件何時算作已讀,以及刪除或移動後跳到哪裡,在「閱讀」頁面設定。"
|
||||
},
|
||||
"send_warnings_description": "復原傳送,以及缺少主旨、忘記附件或預留文字的提醒。",
|
||||
"bulk_delete": {
|
||||
"label": "刪除多封郵件前詢問",
|
||||
"description": "在「郵件清單」頁面設定。"
|
||||
},
|
||||
"sounds": {
|
||||
"label": "音效和未讀標記",
|
||||
"description": "通知音效和分頁上的未讀數,在「通知」頁面設定。"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "關於 {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "在此裝置上登出此帳號。"
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Bulwark Webmail 設定",
|
||||
"subtitle": "在瀏覽器中設定你的網頁郵件執行個體。",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, other {# 封未寄出的郵件將會遺失。}}",
|
||||
"clear_confirm": "清除",
|
||||
"persist_refused": "儲存空間不足時,此瀏覽器可能會刪除保留的郵件。安裝應用程式通常可以避免這種情況。",
|
||||
"persist_retry": "再次請求",
|
||||
"persist_retry": "請瀏覽器保留",
|
||||
"safari_hint": "Safari 會在網站 7 天未造訪後刪除其資料。把 Bulwark 加入主畫面或 Dock,郵件就能保留在此裝置上。",
|
||||
"evicted": "瀏覽器刪除了此裝置上保留的郵件。正在重新下載。",
|
||||
"evicted_lost": "{count, plural, other {# 封未寄出的郵件或變更已遺失。}}",
|
||||
|
||||
+80
-5
@@ -1985,7 +1985,7 @@
|
||||
},
|
||||
"auto_select_reply_identity": {
|
||||
"label": "自动选择回复地址",
|
||||
"description": "回复时自动将发件人地址切换为最初收到该邮件的身份"
|
||||
"description": "回复时,从邮件发送到的地址发出。"
|
||||
},
|
||||
"reply_identity_match": {
|
||||
"label": "接收地址匹配",
|
||||
@@ -2629,7 +2629,7 @@
|
||||
},
|
||||
"refresh_cache": {
|
||||
"label": "Refresh cached data",
|
||||
"description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.",
|
||||
"description": "从服务器重新加载联系人、日历和文件夹。你将保持登录。",
|
||||
"button": "Refresh",
|
||||
"confirm_title": "刷新缓存数据?"
|
||||
},
|
||||
@@ -2642,12 +2642,12 @@
|
||||
},
|
||||
"export_settings": {
|
||||
"label": "导出设置",
|
||||
"description": "将设置导出为 JSON 文件",
|
||||
"description": "下载设置的副本",
|
||||
"button": "导出"
|
||||
},
|
||||
"import_settings": {
|
||||
"label": "导入设置",
|
||||
"description": "从 JSON 文件导入设置",
|
||||
"description": "从文件恢复设置",
|
||||
"button": "导入"
|
||||
},
|
||||
"about": {
|
||||
@@ -3388,6 +3388,81 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uxs_structure": {
|
||||
"tabs": {
|
||||
"offline": "离线",
|
||||
"privacy": "隐私",
|
||||
"about": "关于"
|
||||
},
|
||||
"search": {
|
||||
"signature": "签名 邮件签名 页脚",
|
||||
"sign_out": "退出登录 注销 登出 logout",
|
||||
"app_lock": "PIN 锁定 密码锁 锁屏",
|
||||
"text_size": "字体大小 字号 大字 放大 缩放",
|
||||
"vacation": "自动回复 不在办公室 外出 休假 假期 autoreply",
|
||||
"two_factor": "2FA 双重验证 两步验证 MFA OTP 身份验证器 一次性验证码",
|
||||
"encryption": "S/MIME smime PGP OpenPGP 加密 证书",
|
||||
"password": "密码 修改密码 新密码",
|
||||
"offline": "离线 offline 无网络 无连接 下载 存储 同步",
|
||||
"wipe": "移除 删除 清除 设备 数据",
|
||||
"outbox": "发件箱 未发送 等待 队列",
|
||||
"remote_content": "远程图片 外部内容 跟踪 像素 受信任的发件人",
|
||||
"push": "推送 push 中继 后台 通知",
|
||||
"version": "版本 更新 关于 源代码 GitHub 许可证"
|
||||
},
|
||||
"privacy": {
|
||||
"push_title": "通知",
|
||||
"push_link": {
|
||||
"label": "通知中继",
|
||||
"description": "应用关闭时的通知通过推送中继发送,中继永远看不到你的邮件。可在“通知”页面选择。",
|
||||
"button": "通知"
|
||||
}
|
||||
},
|
||||
"privacy_links": {
|
||||
"images_label": "远程图片和受信任的发件人",
|
||||
"receipts_label": "已读回执",
|
||||
"senders_label": "发件人头像",
|
||||
"description": "这些设置在“隐私”页面。",
|
||||
"button": "隐私"
|
||||
},
|
||||
"a11y_links": {
|
||||
"text": {
|
||||
"label": "密度和按钮标签",
|
||||
"description": "列表行的疏密以及按钮是否显示名称,在“外观”页面设置。"
|
||||
},
|
||||
"light_mode": {
|
||||
"label": "邮件使用浅色背景",
|
||||
"description": "深色模式下邮件是否使用浅色背景,在“阅读”页面设置。"
|
||||
},
|
||||
"calendar": {
|
||||
"label": "日历中的事件预览",
|
||||
"description": "指向事件时是否显示详情,在“日历”页面设置。"
|
||||
},
|
||||
"list": {
|
||||
"label": "会话和预览行",
|
||||
"description": "按会话分组以及每封邮件下方的预览行,在“邮件列表”页面设置。"
|
||||
},
|
||||
"reading": {
|
||||
"label": "标记为已读",
|
||||
"description": "邮件何时算作已读,以及删除或移动后跳到哪里,在“阅读”页面设置。"
|
||||
},
|
||||
"send_warnings_description": "撤销发送,以及缺少主题、忘记附件或占位文字的提醒。",
|
||||
"bulk_delete": {
|
||||
"label": "删除多封邮件前询问",
|
||||
"description": "在“邮件列表”页面设置。"
|
||||
},
|
||||
"sounds": {
|
||||
"label": "声音和未读标记",
|
||||
"description": "通知声音和标签页上的未读数,在“通知”页面设置。"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "关于 {appName}"
|
||||
},
|
||||
"sign_out": {
|
||||
"description": "在此设备上退出此账户。"
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Bulwark Webmail 设置",
|
||||
"subtitle": "在浏览器中配置你的网页邮箱实例。",
|
||||
@@ -5894,7 +5969,7 @@
|
||||
"clear_confirm_unsent": "{count, plural, other {# 封未发送的邮件将丢失。}}",
|
||||
"clear_confirm": "清除",
|
||||
"persist_refused": "存储空间不足时,此浏览器可能会删除保留的邮件。安装应用通常可以避免这种情况。",
|
||||
"persist_retry": "再次请求",
|
||||
"persist_retry": "请浏览器保留",
|
||||
"safari_hint": "Safari 会在网站 7 天未访问后删除其数据。把 Bulwark 添加到主屏幕或程序坞,邮件就能保留在此设备上。",
|
||||
"evicted": "浏览器删除了此设备上保留的邮件。正在重新下载。",
|
||||
"evicted_lost": "{count, plural, other {# 封未发送的邮件或更改已丢失。}}",
|
||||
|
||||
Reference in New Issue
Block a user