Merge branch 'feat/do-ux-p2-settings-search' into feat/do-ux-p2-settings-fix

This commit is contained in:
Linus Rath
2026-11-04 12:39:59 +01:00
49 changed files with 1987 additions and 221 deletions
@@ -174,3 +174,13 @@ describe('the offline banner', () => {
expect(screen.queryByTestId('offline-banner')).toBeNull();
});
});
// settings-p2-18: in a browser the page said "use disk encryption" twice,
// once in the warning and again in the key note below it.
describe('Settings > Offline in a browser', () => {
it('warns about the browser profile once', async () => {
await act(async () => { render(<OfflineSettings />); });
expect(screen.getByTestId('offline-warning').textContent).toBe('warning');
expect(screen.queryByText('browser_note')).toBeNull();
});
});
+2 -1
View File
@@ -68,7 +68,8 @@ export function OfflineSettings() {
<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 />
{/* The warning above already says "use disk encryption"; the browser note said it again (settings-p2-18). */}
<KeyProtectionNotes />
{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>
@@ -52,18 +52,26 @@ beforeEach(() => {
});
describe('AccountSecuritySettings sign-in section', () => {
// settings-p2-18: the heading is "Two-step sign-in", the row's own name, not "Two-factor authentication".
it('heads two-step sign-in with the name of its row', () => {
signIn('basic', false);
render(<AccountSecuritySettings />);
expect(screen.queryByText('totp.section_title')).toBeNull();
expect(screen.getByRole('heading', { name: 'totp.label' })).toBeInTheDocument();
});
it('shows password and two-factor to a password sign-in', () => {
signIn('basic', false);
render(<AccountSecuritySettings />);
expect(screen.getByText('password.title')).toBeInTheDocument();
expect(screen.getByText('totp.section_title')).toBeInTheDocument();
expect(screen.getByRole('heading', { name: 'totp.label' })).toBeInTheDocument();
});
it('shows them to a password plus TOTP sign-in, which runs on OAuth tokens (#1432)', () => {
signIn('oauth', false);
render(<AccountSecuritySettings />);
expect(screen.getByText('password.title')).toBeInTheDocument();
expect(screen.getByText('totp.section_title')).toBeInTheDocument();
expect(screen.getByRole('heading', { name: 'totp.label' })).toBeInTheDocument();
});
it.each([
@@ -74,7 +82,7 @@ describe('AccountSecuritySettings sign-in section', () => {
signIn(authMode, providerSession);
render(<AccountSecuritySettings />);
expect(screen.queryByText('password.title')).toBeNull();
expect(screen.queryByText('totp.section_title')).toBeNull();
expect(screen.queryByRole('heading', { name: 'totp.label' })).toBeNull();
expect(screen.getByText('email_client.title')).toBeInTheDocument();
});
});
@@ -1,6 +1,6 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { MultiAccountPushSettings } from '../multi-account-push';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { AccountPushSwitches } from '../multi-account-push';
import { useAccountStore } from '@/stores/account-store';
import { useAuthStore } from '@/stores/auth-store';
import * as webPush from '@/lib/web-push';
@@ -10,73 +10,82 @@ vi.mock('next-intl', () => ({
values ? `${key} ${JSON.stringify(values)}` : key,
}));
vi.mock('../settings-section', async (importOriginal) => ({
...(await importOriginal<typeof import('../settings-section')>()),
SettingsSection: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
// Push state is stored under the JMAP account id the server assigned: only
// "jmap-a" has a registration here.
vi.mock('@/lib/web-push', () => ({
isWebPushSupported: () => true,
isWebPushEnabled: vi.fn(async (id: string) => id === 'jmap-a'),
enableWebPushForAccounts: vi.fn(),
disableWebPush: vi.fn(),
disableWebPush: vi.fn(async () => undefined),
}));
describe('MultiAccountPushSettings', () => {
it('reads each login\'s push state under its JMAP account id', async () => {
const clients: Record<string, { getAccountId: () => string }> = {
'a@mail.example.com': { getAccountId: () => 'jmap-a' },
'b@mail.example.com': { getAccountId: () => 'jmap-b' },
};
useAccountStore.setState({
accounts: [
{ id: 'a@mail.example.com', username: 'a', email: 'a@example.com', isConnected: true },
{ id: 'b@mail.example.com', username: 'b', email: 'b@example.com', isConnected: true },
] as never,
});
useAuthStore.setState({ getClientForAccount: ((id: string) => clients[id]) as never });
const clients: Record<string, { getAccountId: () => string }> = {
'a@mail.example.com': { getAccountId: () => 'jmap-a' },
'b@mail.example.com': { getAccountId: () => 'jmap-b' },
};
const { container } = render(<MultiAccountPushSettings />);
beforeEach(() => {
vi.clearAllMocks();
useAccountStore.setState({
accounts: [
{ id: 'a@mail.example.com', username: 'a', email: 'a@example.com', displayName: 'Alice', isConnected: true },
{ id: 'b@mail.example.com', username: 'b', email: 'b@example.com', displayName: 'Bob', isConnected: true },
] as never,
});
useAuthStore.setState({ getClientForAccount: ((id: string) => clients[id]) as never, isDemoMode: false });
});
await waitFor(() => expect(container.textContent).toContain('a@example.com · state_on'));
expect(container.textContent).toContain('b@example.com · state_off');
const switchFor = (name: string) => screen.getByRole('switch', { name });
// settings-p2-17 (2026-11-04): a switch per account replaces the checklist
// with "Enable for selected" and "Disable for selected".
describe('AccountPushSwitches', () => {
it('shows one switch per account, set the way push is for it on this device', async () => {
render(<AccountPushSwitches />);
await waitFor(() => expect(switchFor('Alice (a@example.com)').getAttribute('aria-checked')).toBe('true'));
expect(switchFor('Bob (b@example.com)').getAttribute('aria-checked')).toBe('false');
expect(webPush.isWebPushEnabled).toHaveBeenCalledWith('jmap-a');
expect(webPush.isWebPushEnabled).not.toHaveBeenCalledWith('a@mail.example.com');
expect(screen.queryByText('select_all')).toBeNull();
expect(screen.queryByText('enable_selected')).toBeNull();
expect(screen.queryByRole('checkbox')).toBeNull();
});
it('names each account the bulk enable left off, with the reason', async () => {
const clients: Record<string, { getAccountId: () => string }> = {
'a@mail.example.com': { getAccountId: () => 'jmap-a' },
'b@mail.example.com': { getAccountId: () => 'jmap-b' },
'c@mail.example.com': { getAccountId: () => 'jmap-c' },
};
useAccountStore.setState({
accounts: [
{ id: 'a@mail.example.com', username: 'a', email: 'a@example.com', displayName: 'Alice', isConnected: true },
{ id: 'b@mail.example.com', username: 'b', email: 'b@example.com', displayName: 'Bob', isConnected: true },
{ id: 'c@mail.example.com', username: 'c', email: 'c@example.com', displayName: 'Carol', isConnected: true },
] as never,
});
useAuthStore.setState({ getClientForAccount: ((id: string) => clients[id]) as never });
it('turns push on for the one account whose switch was flipped', async () => {
vi.mocked(webPush.enableWebPushForAccounts).mockResolvedValueOnce({ enabled: ['b@mail.example.com'], failed: [] });
const onChanged = vi.fn();
render(<AccountPushSwitches onChanged={onChanged} />);
await waitFor(() => expect(switchFor('Alice (a@example.com)').getAttribute('aria-checked')).toBe('true'));
fireEvent.click(switchFor('Bob (b@example.com)'));
await waitFor(() => expect(onChanged).toHaveBeenCalled());
expect(webPush.enableWebPushForAccounts).toHaveBeenCalledTimes(1);
const [targets] = vi.mocked(webPush.enableWebPushForAccounts).mock.calls[0];
expect(targets.map((t) => t.accountId)).toEqual(['b@mail.example.com']);
});
it('turns it off for that account only', async () => {
render(<AccountPushSwitches />);
await waitFor(() => expect(switchFor('Alice (a@example.com)').getAttribute('aria-checked')).toBe('true'));
fireEvent.click(switchFor('Alice (a@example.com)'));
await waitFor(() => expect(webPush.disableWebPush).toHaveBeenCalledTimes(1));
expect(vi.mocked(webPush.disableWebPush).mock.calls[0][0].client).toBe(clients['a@mail.example.com']);
});
it('says why an account stayed off, under its switch', async () => {
vi.mocked(webPush.enableWebPushForAccounts).mockResolvedValueOnce({
enabled: ['a@mail.example.com'],
failed: [
{ accountId: 'b@mail.example.com', error: new Error('relay unreachable') },
{ accountId: 'c@mail.example.com', error: 'timeout' },
],
enabled: [],
failed: [{ accountId: 'b@mail.example.com', error: new Error('relay unreachable') }],
});
render(<MultiAccountPushSettings />);
await waitFor(() => expect(screen.getByText('select_all')).toBeInTheDocument());
fireEvent.click(screen.getByLabelText('select_all'));
fireEvent.click(screen.getByText('enable_selected'));
render(<AccountPushSwitches />);
await waitFor(() => expect(switchFor('Bob (b@example.com)')).toBeTruthy());
fireEvent.click(switchFor('Bob (b@example.com)'));
const alert = await screen.findByRole('alert');
expect(screen.getByText(/result_partial/).textContent).toContain('"enabled":1,"failed":2');
expect(alert.textContent).toContain('Bob (b@example.com): relay unreachable');
expect(alert.textContent).toContain('Carol (c@example.com): timeout');
expect(alert.textContent).not.toContain('Alice');
expect(alert.textContent).toBe('relay unreachable');
});
it('draws nothing with one account', () => {
useAccountStore.setState({ accounts: [useAccountStore.getState().accounts[0]] });
const { container } = render(<AccountPushSwitches />);
expect(container.textContent).toBe('');
});
});
@@ -256,3 +256,40 @@ describe('NotificationSettings - push errors', () => {
expect(text).toBe('Relay register failed: 502');
});
});
// settings-p2-17 (2026-11-04): with several accounts the page had one switch
// for the account on screen and, below it, a checklist with bulk buttons.
describe('NotificationSettings - several accounts', () => {
beforeEach(async () => {
vi.clearAllMocks();
const { useAccountStore } = await import('@/stores/account-store');
const clients: Record<string, unknown> = { a: fakeClient, b: { getAccountId: () => 'acct-2' } };
useAccountStore.setState({
accounts: [
{ id: 'a', username: 'a', email: 'a@example.com', displayName: 'Alice', isConnected: true },
{ id: 'b', username: 'b', email: 'b@example.com', displayName: 'Bob', isConnected: true },
] as never,
});
useAuthStore.setState({ client: fakeClient, username: 'a@example.com', isDemoMode: false, getClientForAccount: ((id: string) => clients[id]) as never });
usePolicyStore.setState({
...usePolicyStore.getState(),
policy: {},
isSettingLocked: () => false,
isSettingHidden: () => false,
} as never);
});
afterEach(async () => {
const { useAccountStore } = await import('@/stores/account-store');
useAccountStore.setState({ accounts: [] });
});
it('has one switch per account instead of the switch for this account and a checklist', async () => {
const { findByRole, queryByText, queryByRole } = render(<NotificationSettings />);
expect(await findByRole('switch', { name: 'Alice (a@example.com)' })).toBeTruthy();
expect(await findByRole('switch', { name: 'Bob (b@example.com)' })).toBeTruthy();
expect(queryByText('push.enable')).toBeNull();
expect(queryByText('select_all')).toBeNull();
expect(queryByRole('checkbox')).toBeNull();
});
});
@@ -0,0 +1,185 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react';
import { useAuthStore } from '@/stores/auth-store';
import { usePolicyStore } from '@/stores/policy-store';
import { useSettingsStore } from '@/stores/settings-store';
// Usability pass 2, 2026-11-04: Notifications, Privacy, Backup & reset and
// Troubleshooting in the desktop app and in a browser (settings-p2-4, -9,
// -18; desktop-p2-4). Labels are keys here (next-intl mock).
const config = vi.hoisted(() => ({ settingsSyncEnabled: true }));
vi.mock('@/hooks/use-config', () => ({ useConfig: () => ({ appName: 'Bulwark', settingsSyncEnabled: config.settingsSyncEnabled }) }));
vi.mock('../desktop-telemetry-settings', () => ({ DesktopTelemetrySettings: () => null }));
vi.mock('@/components/mail-transfer/mail-backup-settings', () => ({ MailBackupSettings: () => null }));
vi.mock('../debug-settings', () => ({ DebugSettings: () => null }));
vi.mock('@/lib/web-push', () => ({
WebPushUnsupportedError: class WebPushUnsupportedError extends Error {},
isWebPushSupported: () => false,
isWebPushEnabled: vi.fn(async () => false),
enableWebPush: vi.fn(),
enableWebPushForAccounts: vi.fn(),
disableWebPush: vi.fn(),
listPushDevices: vi.fn(async () => []),
mailAccountsWithoutPush: vi.fn(() => []),
revokePushDevice: vi.fn(),
serverSupportsEmailPush: vi.fn(() => false),
}));
const { NotificationSettings } = await import('../notification-settings');
const { PrivacySettings } = await import('../privacy-settings');
const { AboutDataSettings } = await import('../about-data-settings');
const { DesktopDevToolsSettings } = await import('../desktop-devtools-settings');
const { AdvancedSettingsProvider } = await import('../settings-section');
const osStatus = {
settings: {
closeToTray: true, startAtLogin: false, startMinimized: false, attentionOnNewMail: true,
quietHoursEnabled: false, quietHoursStart: '22:00', quietHoursEnd: '07:00', showShortcut: null,
},
pausedUntil: null,
shortcutActive: false,
};
function installBridge() {
(window as unknown as { bulwarkDesktop: unknown }).bulwarkDesktop = {
version: 1,
platform: 'win32',
os: {
getStatus: vi.fn(async () => osStatus),
defaultAppStatus: vi.fn(async () => ({ mailto: false })),
onStatusChange: vi.fn(() => () => {}),
setSettings: vi.fn(async () => osStatus),
openDefaultAppSettings: vi.fn(),
},
devTools: {
get: vi.fn(async () => ({ on: false, forcedOff: false, locked: false })),
set: vi.fn(),
onChange: vi.fn(() => () => {}),
},
};
}
async function settle() {
await act(async () => { await new Promise((r) => setTimeout(r, 20)); });
}
const realUserAgent = navigator.userAgent;
function setUserAgent(ua: string) {
Object.defineProperty(navigator, 'userAgent', { value: ua, configurable: true });
}
beforeEach(() => {
usePolicyStore.setState({
...usePolicyStore.getState(),
policy: {},
isSettingLocked: () => false,
isSettingHidden: () => false,
isFeatureEnabled: () => true,
} as never);
useAuthStore.setState({ client: null, username: null } as never);
useSettingsStore.setState({ showAdvancedSettings: false, settingsSyncDisabled: false });
config.settingsSyncEnabled = true;
});
afterEach(() => {
cleanup();
delete (window as unknown as { bulwarkDesktop?: unknown }).bulwarkDesktop;
setUserAgent(realUserAgent);
});
describe('Notifications when the app is closed', () => {
it('in the desktop app: no browser push section, one line about the tray instead', async () => {
installBridge();
render(<NotificationSettings />);
await settle();
expect(screen.queryByText('push.title')).toBeNull();
expect(screen.queryByText('push.enable')).toBeNull();
expect(screen.queryByText(/ios_hint|status_unsupported|push_unsupported/)).toBeNull();
expect(screen.getByText('closed_label')).toBeTruthy();
expect(screen.getByText('closed_description')).toBeTruthy();
// The section with the taskbar and quiet hours isn't called "Desktop app", like General's.
expect(screen.getByText('title')).toBeTruthy();
});
it('in a browser that can\'t: one sentence, without the iPhone hint off iOS', async () => {
setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) Firefox/140.0');
render(<NotificationSettings />);
await settle();
expect(screen.getByText('push_unsupported')).toBeTruthy();
expect(screen.queryByText(/push\.ios_hint/)).toBeNull();
expect(screen.queryByText(/status_unsupported/)).toBeNull();
});
it('on an iPhone: the home screen hint after it, as its own sentence', async () => {
setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) Safari/604.1');
render(<NotificationSettings />);
await settle();
expect(screen.getByText('push_unsupported push.ios_hint')).toBeTruthy();
});
});
describe('Privacy', () => {
it('links to the push relay in a browser only', async () => {
const browser = render(<PrivacySettings />);
await settle();
expect(screen.getByText('push_title')).toBeTruthy();
browser.unmount();
installBridge();
render(<PrivacySettings />);
await settle();
expect(screen.queryByText('push_title')).toBeNull();
});
});
describe('Backup & reset', () => {
it('says "other devices" in the app, not "other browsers"', async () => {
installBridge();
render(<AboutDataSettings />);
await settle();
expect(screen.getByText('backup_description_app')).toBeTruthy();
expect(screen.queryByText('sections.backup_description')).toBeNull();
});
it('says what a reset keeps, and whether other devices change', async () => {
render(<AboutDataSettings />);
await settle();
fireEvent.click(screen.getByText('reset_settings.button'));
expect(screen.getByText('reset.message reset.sync_on')).toBeTruthy();
cleanup();
useSettingsStore.setState({ settingsSyncDisabled: true });
render(<AboutDataSettings />);
await settle();
fireEvent.click(screen.getByText('reset_settings.button'));
expect(screen.getByText('reset.message reset.sync_off')).toBeTruthy();
});
it('keeps the choice to sync settings through a reset', () => {
useSettingsStore.setState({ settingsSyncDisabled: true, showAdvancedSettings: true });
useSettingsStore.getState().resetToDefaults();
expect(useSettingsStore.getState().settingsSyncDisabled).toBe(true);
expect(useSettingsStore.getState().showAdvancedSettings).toBe(false);
});
});
describe('Developer tools in the desktop app', () => {
it('show only with advanced settings on', async () => {
installBridge();
const page = () => (
<AdvancedSettingsProvider>
<DesktopDevToolsSettings />
</AdvancedSettingsProvider>
);
render(page());
await settle();
expect(screen.queryByTestId('desktop-devtools-toggle')).toBeNull();
cleanup();
useSettingsStore.setState({ showAdvancedSettings: true });
render(page());
await settle();
expect(screen.getByTestId('desktop-devtools-toggle')).toBeTruthy();
});
});
@@ -58,7 +58,8 @@ async function openResult(query: string, label: string) {
describe('settings search and the Advanced switch', () => {
it('leaves Advanced off for a row that is not an advanced one', async () => {
await openResult('favicons', 'Sender favicons');
// settings-p2-18: the row is named like its section now, "Sender pictures".
await openResult('company websites', 'Pictures from company websites');
expect(useSettingsStore.getState().showAdvancedSettings).toBe(false);
});
@@ -1,5 +1,5 @@
import 'fake-indexeddb/auto';
import { render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { NextIntlClientProvider } from 'next-intl';
import en from '@/locales/en/common.json';
@@ -11,6 +11,8 @@ import en from '@/locales/en/common.json';
// 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.
// settings-p2-5 (2026-11-04): and in the desktop app, with its bridge
// there, so the app's own rows (tray, quiet hours, menu bar) are found too.
vi.unmock('next-intl');
vi.mock('@/components/plugins/plugin-iframe-slot', () => ({ PluginIframeSlot: () => null }));
@@ -73,6 +75,42 @@ const OVERRIDES: Record<string, () => unknown> = {
getShareTargets: async () => ({ targets: [], failed: [] }), listPrincipals: async () => ({ principals: [], status: 'ok' }),
getPrincipals: async () => [], listArchivedEmails: async () => ({ items: [], nextPosition: null }),
};
/** Pages with rows of the desktop app's own, or browser rows it hides. */
const DESKTOP_PAGES = new Set(['general', 'notifications', 'privacy', 'backup']);
/** The desktop app's bridge, answering as Windows does, with every switch off. */
const DESKTOP_BRIDGE = {
version: 1,
platform: 'win32',
mode: 'standalone',
windows: {},
menu: { bar: { setPinned: () => {}, onEvent: () => () => {} } },
os: {
getStatus: async () => ({
settings: {
closeToTray: false, startAtLogin: false, startMinimized: false, attentionOnNewMail: false,
quietHoursEnabled: false, quietHoursStart: '22:00', quietHoursEnd: '07:00', showShortcut: null,
},
pausedUntil: null,
shortcutActive: false,
}),
defaultAppStatus: async () => ({ registered: true, mailto: false, webcal: false }),
onStatusChange: () => () => {},
setSettings: async () => { throw new Error('not here'); },
openDefaultAppSettings: async () => ({ registered: true, mailto: false, webcal: false }),
},
updates: {
state: async () => ({ version: '1.0.0', channel: 'stable', channelLocked: false, allowed: true, ability: 'install', perMachine: false, status: 'idle' }),
onChanged: () => () => {},
},
devTools: { get: async () => ({ on: false, forcedOff: false, locked: false }), set: async () => ({ on: false, forcedOff: false, locked: false }), onChange: () => () => {} },
privacy: {
get: async () => ({ telemetry: { on: false, forcedOff: false }, crashReports: { on: false, forcedOff: false, uploadUrl: '' } }),
listCrashes: async () => [],
onChange: () => () => {},
},
};
const CLIENT = new Proxy({}, {
get: (_t, prop) => (prop === 'then' ? undefined : (OVERRIDES[String(prop)] ?? (() => null))),
});
@@ -88,7 +126,7 @@ const TAB_PAGES: Record<string, string[]> = {
};
describe('settings search index', () => {
it('has a row for every entry on its page', { timeout: 120_000 }, () => {
it('has a row for every entry on its page', { timeout: 180_000 }, async () => {
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('no network')));
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);
@@ -103,6 +141,7 @@ describe('settings search index', () => {
const realMatchMedia = window.matchMedia;
const variants: Record<string, () => void> = {
base: () => {},
desktop: () => { window.bulwarkDesktop = DESKTOP_BRIDGE as never; },
touch: () => {
window.matchMedia = ((q: string) => ({ ...realMatchMedia(q), matches: q.includes('coarse') })) as typeof window.matchMedia;
},
@@ -120,12 +159,18 @@ describe('settings search index', () => {
useSettingsStore.setState(settingsInitial, true);
useAccountStore.setState(accountsInitial, true);
window.matchMedia = realMatchMedia;
window.bulwarkDesktop = undefined;
apply();
out[variant] = {};
for (const [tab, Page] of Object.entries(pages)) {
const labels = (adv: boolean) => {
// The desktop app changes only these pages; the others are the browser's.
if (variant === 'desktop' && !DESKTOP_PAGES.has(tab)) {
out[variant][tab] = out.base[tab];
continue;
}
const draw = (adv: boolean) => {
useSettingsStore.setState({ showAdvancedSettings: adv });
const { container, unmount } = render(
return render(
<NextIntlClientProvider locale="en" messages={en}>
<TourProvider>
<AdvancedSettingsProvider>
@@ -134,14 +179,23 @@ describe('settings search index', () => {
</TourProvider>
</NextIntlClientProvider>,
);
};
const read = ({ container, unmount }: ReturnType<typeof draw>) => {
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, sections };
};
// The desktop rows draw once the bridge has answered. Elsewhere nothing
// waits: the pages' own loads would answer from the stub client.
const labels = async (adv: boolean) => {
const view = draw(adv);
if (variant === 'desktop') await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); });
return read(view);
};
try {
const on = labels(true);
const off = labels(false);
const on = variant === 'desktop' ? await labels(true) : read(draw(true));
const off = variant === 'desktop' ? await labels(false) : read(draw(false));
out[variant][tab] = { on: on.found, off: off.found, sections: on.sections };
} catch (e) {
out[variant][tab] = { on: [], off: [], sections: [], error: String(e) };
@@ -149,13 +203,17 @@ describe('settings search index', () => {
}
}
window.matchMedia = realMatchMedia;
window.bulwarkDesktop = undefined;
const problems: string[] = [];
for (const [tab, entries] of Object.entries(tabSearchEntries)) {
for (const entry of entries) {
// Rows that name the app show the stock name, as the index does.
const text = withAppName(getByPath(en, entry.label), 'Bulwark') as string;
const variant = out[entry.when ?? 'base'];
// The desktop app's rows are all drawn in the Windows variant; a Mac row there is the Dock's.
const variantName = entry.when?.startsWith('desktop') ? 'desktop' : entry.when === 'browser' ? 'base' : (entry.when ?? 'base');
if (entry.when === 'desktopMac') continue;
const variant = out[variantName];
const parts = TAB_PAGES[tab].map((page) => variant[page]);
const errors = parts.filter((part) => part.error).map((part) => part.error);
if (errors.length) { problems.push(`${tab}: ${errors.join('; ')}`); continue; }
@@ -167,13 +225,20 @@ describe('settings search index', () => {
}
}
}
// A browser's row isn't in the desktop app (the push section, the browser's mail links).
for (const [tab, entries] of Object.entries(tabSearchEntries)) {
for (const entry of entries.filter((e) => e.when === 'browser')) {
const text = withAppName(getByPath(en, entry.label), 'Bulwark') as string;
if (TAB_PAGES[tab].some((page) => out.desktop[page].on.includes(text))) problems.push(`${tab}: ${entry.label} is drawn in the desktop app`);
}
}
// 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) {
for (const section of [...out.base[page].sections, ...out.desktop[page].sections]) {
if (!indexed.has(section)) problems.push(`${tab}: section "${section}" has no search entry`);
}
}
@@ -181,3 +246,14 @@ describe('settings search index', () => {
expect([...new Set(problems)]).toEqual([]);
});
});
// Rows only a browser draws are not in the desktop app, and the other way round.
describe('settings search index, browser and desktop app', () => {
it('marks the rows each draws', () => {
const when = (label: string) => Object.values(tabSearchEntries).flat().find((entry) => entry.label === label)?.when;
expect(when('settings.notifications.push.title')).toBe('browser');
expect(when('desktopOs.close_to_tray_label')).toBe('desktop');
expect(when('desktopOs.quiet_hours_label')).toBe('desktop');
expect(when('desktopMenuBar.always_show_label')).toBe('desktopMenuBar');
});
});
+9 -2
View File
@@ -18,6 +18,7 @@ import { toast } from '@/stores/toast-store';
import { DebugSettings } from './debug-settings';
import { MailBackupSettings } from '@/components/mail-transfer/mail-backup-settings';
import { productName } from '@/lib/product-name';
import { isDesktopApp } from '@/lib/desktop/bridge';
const APP_VERSION = process.env.NEXT_PUBLIC_APP_VERSION || "0.0.0";
const GIT_COMMIT = process.env.NEXT_PUBLIC_GIT_COMMIT || "unknown";
@@ -157,6 +158,12 @@ export function AboutDataSettings() {
const { settingsSyncEnabled } = useConfig();
const [showResetConfirm, setShowResetConfirm] = useState(false);
const [showRefreshConfirm, setShowRefreshConfirm] = useState(false);
const tP2 = useTranslations('uxp2_settings');
// The app has no "other browsers" (settings-p2-4).
const [inApp, setInApp] = useState(false);
useEffect(() => setInApp(isDesktopApp()), []);
// A reset keeps the sync choice, so the other devices change only when sync is on (settings-p2-18).
const syncOn = settingsSyncEnabled && !settingsSyncDisabled;
const fileInputRef = useRef<HTMLInputElement>(null);
const { isFeatureEnabled } = usePolicyStore();
const handleExport = () => {
@@ -202,7 +209,7 @@ export function AboutDataSettings() {
return (
<div className="space-y-8">
<SettingsSection id="backup" title={t('sections.backup')} description={t('sections.backup_description')}>
<SettingsSection id="backup" title={t('sections.backup')} description={inApp ? tP2('backup_description_app') : t('sections.backup_description')}>
{settingsSyncEnabled && (
<SettingItem label={t('settings_sync.label')} description={t('settings_sync.description')} term="sync">
<ToggleSwitch checked={!settingsSyncDisabled} onChange={(checked) => updateSetting('settingsSyncDisabled', !checked)} />
@@ -264,7 +271,7 @@ export function AboutDataSettings() {
onClose={() => setShowResetConfirm(false)}
onConfirm={handleReset}
title={t('reset_settings.confirm_title')}
message={t('reset_settings.description')}
message={`${tP2('reset.message')} ${syncOn ? tP2('reset.sync_on') : tP2('reset.sync_off')}`}
confirmText={t('reset_settings.button')}
variant="destructive"
/>
@@ -1501,7 +1501,8 @@ export function AccountSecuritySettings() {
<div>
<div className="flex items-center gap-2 mb-3">
<Shield className="w-4 h-4 text-muted-foreground" />
<h4 data-search-label={t('totp.section_title')} className="text-sm font-medium text-foreground">{t('totp.section_title')}</h4>
{/* Named like its row, "Two-step sign-in", not "Two-factor authentication" (settings-p2-18). */}
<h4 data-search-label={t('totp.label')} className="text-sm font-medium text-foreground">{t('totp.label')}</h4>
<TermInfo id="two_factor" />
</div>
<TotpSection />
@@ -37,7 +37,8 @@ export function DesktopDevToolsSettings() {
const description = state.forcedOff ? t("forced_off") : state.locked ? t("locked") : t("description", { shortcut });
return (
<SettingsSection id="desktop-troubleshooting" title={t("section_title")}>
// Only with "Show advanced settings" on: nobody needs it to read mail (settings-p2-9).
<SettingsSection id="desktop-troubleshooting" advanced title={t("section_title")}>
<SettingItem label={t("label", { shortcut })} description={description} locked={state.forcedOff}>
<ToggleSwitch
checked={state.on}
+14 -1
View File
@@ -7,6 +7,9 @@ import { getDesktopBridge } from "@/lib/desktop/bridge";
import type { DesktopOsDefaultAppStatus, DesktopOsSettings as Settings, DesktopOsStatus } from "@/lib/desktop/types";
import { DesktopQuickComposeSetting } from "./desktop-quick-compose-setting";
import { SettingItem, SettingsSection, ToggleSwitch } from "./settings-section";
import { goToSettings, SettingsLinkRow } from "./privacy-settings";
import { productName } from "@/lib/product-name";
import { useConfig } from "@/hooks/use-config";
/** On Windows and Linux this is what a press of the keys shows; macOS maps it to Cmd. */
const SHOW_SHORTCUT = "CommandOrControl+Shift+B";
@@ -23,6 +26,9 @@ function shortcutLabel(platform: string | undefined): string {
*/
export function DesktopOsSettings({ part = "app" }: { part?: "app" | "notifications" } = {}) {
const t = useTranslations("desktopOs");
const tP2 = useTranslations("uxp2_settings.desktop_notifications");
const tTabs = useTranslations("settings.tabs");
const appName = productName(useConfig().appName);
const [status, setStatus] = useState<DesktopOsStatus | null>(null);
const [defaultApp, setDefaultApp] = useState<DesktopOsDefaultAppStatus | null>(null);
const bridge = getDesktopBridge();
@@ -52,7 +58,14 @@ export function DesktopOsSettings({ part = "app" }: { part?: "app" | "notificati
if (part === "notifications") {
return (
<SettingsSection id="desktop-notifications" title={t("section_title")}>
<SettingsSection id="desktop-notifications" title={tP2("title")}>
{/* The app tells about new mail as long as it runs; a browser's push section has nothing to add (settings-p2-4). */}
<SettingsLinkRow
label={tP2("closed_label")}
description={tP2("closed_description", { appName, page: tTabs("general") })}
button={tTabs("general")}
onClick={() => goToSettings("general", "desktop-app")}
/>
<SettingItem
label={bridge?.platform === "darwin" ? t("attention_mac_label") : t("attention_label")}
description={t("attention_description")}
+65 -113
View File
@@ -2,57 +2,50 @@
import { useCallback, useEffect, useState } from 'react';
import { useTranslations } from 'next-intl';
import { Loader2 } from '@/components/icons';
import { Button } from '@/components/ui/button';
import { SettingsSection } from './settings-section';
import { SettingItem, ToggleSwitch } from './settings-section';
import { useAccountStore } from '@/stores/account-store';
import { useAuthStore } from '@/stores/auth-store';
import { useSettingsStore } from '@/stores/settings-store';
import { disableWebPush, enableWebPushForAccounts, isWebPushEnabled, isWebPushSupported } from '@/lib/web-push';
import { webPushErrorKey } from '@/lib/web-push-errors';
interface FailedAccount {
id: string;
name: string;
email: string;
reason: string;
/** The accounts signed in here with a live connection: each can have push on this device. */
export function usePushAccounts() {
const accounts = useAccountStore((s) => s.accounts);
// Re-read the client map whenever a connection appears or drops.
useAuthStore((s) => s.connectedAccountsRevision);
const isDemoMode = useAuthStore((s) => s.isDemoMode);
if (isDemoMode || !isWebPushSupported()) return [];
return accounts.filter((a) => a.isConnected && useAuthStore.getState().getClientForAccount(a.id));
}
/**
* Turn push on or off for several accounts at once.
*
* The per-account panel above only ever addresses the account on screen, so
* arming a fresh device used to mean switching account N times. One browser
* subscription already serves every account (see `enableWebPushForAccounts`);
* what was missing was a place to register them all against it in one go.
* One switch per account for notifications while the app is closed, under
* "Notifications when … is closed" (settings-p2-17). It replaces a checklist
* with "Enable for selected" and "Disable for selected": with a switch each,
* an account's state is what its switch shows. One browser subscription
* serves every account (see `enableWebPushForAccounts`).
*/
export function MultiAccountPushSettings({ relayBaseUrl }: { relayBaseUrl?: string }) {
const t = useTranslations('settings.notifications.push_all');
export function AccountPushSwitches({ relayBaseUrl, disabled = false, onChanged }: {
relayBaseUrl?: string;
/** No relay to send through: the switches show their state but can't turn on. */
disabled?: boolean;
/** After a switch changed something, so the page can re-read its own state. */
onChanged?: () => void;
}) {
const tPush = useTranslations('settings.notifications.push');
const connected = usePushAccounts();
const revision = useAuthStore((s) => s.connectedAccountsRevision);
const [enabled, setEnabled] = useState<Record<string, boolean>>({});
const [busy, setBusy] = useState<string | null>(null);
// The reason an account's switch didn't move, shown under it.
const [failures, setFailures] = useState<Record<string, string>>({});
const failureReason = (error: unknown): string => {
const key = webPushErrorKey(error);
if (key) return tPush(key);
return error instanceof Error && error.message ? error.message : String(error);
};
const failureOf = (account: (typeof accounts)[number], error: unknown): FailedAccount => ({
id: account.id,
name: account.displayName || account.label || account.username,
email: account.email || account.username,
reason: failureReason(error),
});
const accounts = useAccountStore(s => s.accounts);
// Re-read the client map whenever a connection appears or drops.
const revision = useAuthStore(s => s.connectedAccountsRevision);
const isDemoMode = useAuthStore(s => s.isDemoMode);
const [enabled, setEnabled] = useState<Record<string, boolean>>({});
const [chosen, setChosen] = useState<Set<string>>(new Set());
const [busy, setBusy] = useState(false);
const [notice, setNotice] = useState('');
// Which accounts the last bulk enable left off, and why: a bare count
// leaves the user guessing which login to retry.
const [failures, setFailures] = useState<FailedAccount[]>([]);
const connected = accounts.filter(a => a.isConnected && useAuthStore.getState().getClientForAccount(a.id));
const refresh = useCallback(async () => {
// Push state is stored under the JMAP account id the server assigned (see
@@ -68,98 +61,57 @@ export function MultiAccountPushSettings({ relayBaseUrl }: { relayBaseUrl?: stri
useEffect(() => { void refresh(); }, [refresh, revision]);
if (!isWebPushSupported() || isDemoMode || connected.length < 2) return null;
if (connected.length < 2) return null;
const toggle = (id: string, on: boolean) => setChosen(prev => {
const next = new Set(prev);
if (on) next.add(id); else next.delete(id);
return next;
});
const run = async (action: 'enable' | 'disable') => {
const auth = useAuthStore.getState();
const targets = connected.filter(a => chosen.has(a.id));
if (!targets.length) return;
setBusy(true); setNotice(''); setFailures([]);
const change = async (account: (typeof connected)[number], on: boolean) => {
const client = useAuthStore.getState().getClientForAccount(account.id);
if (!client) return;
setBusy(account.id);
setFailures(({ [account.id]: _dropped, ...rest }) => rest);
try {
if (action === 'enable') {
if (on) {
const outcome = await enableWebPushForAccounts(
targets.map(a => ({ accountId: a.id, client: auth.getClientForAccount(a.id)!, accountLabel: a.username })),
[{ accountId: account.id, client, accountLabel: account.username }],
{
relayBaseUrl,
inboxOnly: useSettingsStore.getState().pushNotifyInboxOnly,
mutes: useSettingsStore.getState().notificationMutes,
},
);
setNotice(outcome.failed.length
? t('result_partial', { enabled: outcome.enabled.length, failed: outcome.failed.length })
: t('result_enabled', { count: outcome.enabled.length }));
setFailures(outcome.failed.map(({ accountId, error }) => {
const account = targets.find(a => a.id === accountId);
return {
id: accountId,
name: account ? (account.displayName || account.label || account.username) : accountId,
email: account ? (account.email || account.username) : '',
reason: failureReason(error),
};
}));
const failed = outcome.failed[0];
if (failed) setFailures((prev) => ({ ...prev, [account.id]: failureReason(failed.error) }));
} else {
// Every account ends up off here; one the server or relay couldn't be
// told about is named, since it may keep getting notifications for days.
const failed: FailedAccount[] = [];
for (const account of targets) {
await disableWebPush({ client: auth.getClientForAccount(account.id)!, relayBaseUrl })
.catch((error: unknown) => { failed.push(failureOf(account, error)); });
}
setNotice(t('result_disabled', { count: targets.length }));
setFailures(failed);
await disableWebPush({ client, relayBaseUrl });
}
setChosen(new Set());
} catch (error) {
setFailures((prev) => ({ ...prev, [account.id]: failureReason(error) }));
} finally {
setBusy(false);
setBusy(null);
await refresh();
onChanged?.();
}
};
const allChosen = connected.length > 0 && connected.every(a => chosen.has(a.id));
return <SettingsSection id="push-all" title={t('title')} description={t('description')}>
<div className="space-y-3">
<label className="flex items-center gap-2 text-sm font-medium">
<input type="checkbox" disabled={busy} checked={allChosen}
onChange={e => setChosen(e.target.checked ? new Set(connected.map(a => a.id)) : new Set())} />
{t('select_all')}
</label>
<ul className="space-y-2">
{connected.map(account => <li key={account.id}>
<label className="flex items-start gap-2 ps-6 text-sm">
<input type="checkbox" className="mt-1" disabled={busy} checked={chosen.has(account.id)}
onChange={e => toggle(account.id, e.target.checked)} />
<span className="min-w-0 wrap-anywhere">
<span className="block">{account.displayName || account.label || account.username}</span>
<span className="block text-xs text-muted-foreground">
<bdi dir="ltr">{account.email || account.username}</bdi> · {enabled[account.id] ? t('state_on') : t('state_off')}
</span>
</span>
</label>
</li>)}
</ul>
<div className="flex flex-wrap items-center gap-2">
<Button type="button" onClick={() => void run('enable')} disabled={busy || !chosen.size}>
{busy && <Loader2 className="me-2 h-4 w-4 animate-spin" />}{t('enable_selected')}
</Button>
<Button type="button" variant="outline" onClick={() => void run('disable')} disabled={busy || !chosen.size}>
{t('disable_selected')}
</Button>
</div>
{notice && <p className="text-sm text-muted-foreground">{notice}</p>}
{failures.length > 0 && <ul role="alert" className="space-y-1 text-sm text-destructive">
{failures.map(f => <li key={f.id}>
<span className="font-medium">{f.name}</span>
{f.email && f.email !== f.name && <span> (<bdi dir="ltr">{f.email}</bdi>)</span>}
<span>: {f.reason}</span>
</li>)}
</ul>}
</div>
</SettingsSection>;
return (
<>
{connected.map((account) => {
const name = account.displayName || account.label || account.username;
const email = account.email || account.username;
const failure = failures[account.id];
return (
<div key={account.id} data-testid="account-push-row">
<SettingItem label={name} description={email !== name ? email : undefined}>
<ToggleSwitch
checked={!!enabled[account.id]}
disabled={busy !== null || (disabled && !enabled[account.id])}
ariaLabel={email !== name ? `${name} (${email})` : name}
onChange={(on) => void change(account, on)}
/>
</SettingItem>
{failure && <p role="alert" className="-mt-2 pb-2 text-xs text-destructive">{failure}</p>}
</div>
);
})}
</>
);
}
+34 -6
View File
@@ -15,7 +15,8 @@ import { usePolicyStore } from '@/stores/policy-store';
import { useAuthStore } from '@/stores/auth-store';
import { useAccountStore } from '@/stores/account-store';
import { ConfirmDialog } from '@/components/ui/confirm-dialog';
import { MultiAccountPushSettings } from './multi-account-push';
import { AccountPushSwitches, usePushAccounts } from './multi-account-push';
import { isDesktopApp } from '@/lib/desktop/bridge';
import { productName } from '@/lib/product-name';
import { useConfig } from '@/hooks/use-config';
import { useConfirmDialog } from '@/hooks/use-confirm-dialog';
@@ -52,12 +53,23 @@ type DevicesState =
| { kind: 'loaded'; devices: PushDevice[] }
| { kind: 'error' };
/** iPhone or iPad, where Safari only pushes for a site on the home screen. iPadOS says it's a Mac with touch. */
function isAppleMobile(): boolean {
if (typeof navigator === 'undefined') return false;
return /iPhone|iPad|iPod/.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
}
export function NotificationSettings() {
const t = useTranslations('settings.notifications');
const tAppearance = useTranslations('settings.appearance');
const tUnread = useTranslations('account_unread');
const severalAccounts = useAccountStore((s) => s.accounts.length > 1);
const tThirdParty = useTranslations('third_party');
const tP2 = useTranslations('uxp2_settings');
// The desktop app tells about new mail while it runs, in the tray too; the
// browser's push section talks about websites and Safari (settings-p2-4).
const inApp = isDesktopApp();
const pushAccounts = usePushAccounts();
const appName = productName(useConfig().appName);
// The relay list depends on it; re-render once the config is known (#903).
const noThirdParty = useNoThirdPartyTraffic();
@@ -86,11 +98,15 @@ export function NotificationSettings() {
const supported = typeof window !== 'undefined' && isWebPushSupported() && !isDemoMode;
// "Inbox only" is enforced by the server's emailPush filter; without that
// capability (Stalwart < 0.16.16) the toggle would do nothing.
const inboxOnlyAvailable = supported && !!client && serverSupportsEmailPush(client);
const inboxOnlyAvailable = supported && !inApp && !!client && serverSupportsEmailPush(client);
const [pushStatus, setPushStatus] = useState<PushStatus>(
supported ? { kind: 'idle' } : { kind: 'unsupported' },
);
const [devices, setDevices] = useState<DevicesState>({ kind: 'idle' });
// Bumped when an account's own switch changed push, so this account's state is read again.
const [pushRecheck, setPushRecheck] = useState(0);
// Several accounts connected: a switch each instead of one for the account on screen (settings-p2-17).
const perAccount = supported && pushAccounts.length > 1;
const [revokingId, setRevokingId] = useState<string | null>(null);
// Relay URLs come from admin policy only - users pick one of the offered
@@ -133,7 +149,7 @@ export function NotificationSettings() {
window.removeEventListener('focus', onVisible);
document.removeEventListener('visibilitychange', onVisible);
};
}, [supported, client, pushStatus.kind]);
}, [supported, client, pushStatus.kind, pushRecheck]);
// The device list is worth loading even when push is off on this browser -
// revoking a stale registration left on another device is exactly what you
@@ -156,7 +172,8 @@ export function NotificationSettings() {
// In the static Lite build push is off by design (no service worker, see
// components/service-worker-registration.tsx), not a browser limitation.
const statusDescription = pushStatus.kind === 'unsupported'
? (IS_LITE ? t('push.status_lite') : isDemoMode ? t('push.status_demo') : `${t('push.status_unsupported')} ${t('push.ios_hint')}`)
// Two sentences, and the home-screen hint only where it applies (settings-p2-4).
? (IS_LITE ? t('push.status_lite') : isDemoMode ? t('push.status_demo') : isAppleMobile() ? `${tP2('push_unsupported')} ${t('push.ios_hint')}` : tP2('push_unsupported'))
: noRelay && !pushEnabled
? tThirdParty('push_needs_relay')
: busy
@@ -431,7 +448,18 @@ export function NotificationSettings() {
{/* The desktop app's taskbar flash and quiet hours. Nothing in a browser. */}
<DesktopOsSettings part="notifications" />
{!inApp && (
<SettingsSection id="push" title={t('push.title', { appName })} description={t('push.description')} term="push">
{perAccount ? (
<>
{noRelay && <p className="text-xs text-muted-foreground">{tThirdParty('push_needs_relay')}</p>}
<AccountPushSwitches
relayBaseUrl={activeRelayUrl}
disabled={noRelay}
onChanged={() => { setPushRecheck((n) => n + 1); void refreshDevices(); }}
/>
</>
) : (
<SettingItem label={t('push.enable')} description={statusDescription}>
<div className="flex items-center gap-2">
{pushEnabled && (
@@ -446,6 +474,7 @@ export function NotificationSettings() {
/>
</div>
</SettingItem>
)}
{sharedMailboxesWithoutPush && (
<p className="text-xs text-muted-foreground">{t('push.shared_mailboxes_no_push')}</p>
@@ -476,8 +505,7 @@ export function NotificationSettings() {
</p>
)}
</SettingsSection>
<MultiAccountPushSettings relayBaseUrl={activeRelayUrl} />
)}
{client && (
<SettingsSection id="devices" advanced title={t('push.devices_title')} description={t('push.devices_desc')}>
+17 -3
View File
@@ -15,6 +15,9 @@ 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';
import { isDesktopApp } from '@/lib/desktop/bridge';
import { productName } from '@/lib/product-name';
import { useConfig } from '@/hooks/use-config';
/** Opens another settings page (and section) the way deep links do. */
export function goToSettings(tab: string, section?: string) {
@@ -160,13 +163,15 @@ function ReadReceiptsSection() {
function SenderPicturesSection() {
const tPage = useTranslations('settings.message_list');
const tAdvanced = useTranslations('settings.advanced');
const tP2 = useTranslations('uxp2_settings');
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')}>
// Named like its section, "Sender pictures", not "favicons" (settings-p2-18).
<SettingItem label={tP2('sender_favicons.label')} description={tP2('sender_favicons.description')}>
<ToggleSwitch checked={senderFavicons} onChange={(checked) => updateSetting('senderFavicons', checked)} />
</SettingItem>
)}
@@ -200,20 +205,29 @@ function SenderPicturesSection() {
*/
export function PrivacySettings() {
const t = useTranslations('uxs_structure.privacy');
const tP2 = useTranslations('uxp2_settings.privacy_push');
const tTabs = useTranslations('settings.tabs');
const appName = productName(useConfig().appName);
// The desktop app has no push relay: it tells about new mail while it runs (settings-p2-4).
const [inApp, setInApp] = useState(false);
useEffect(() => setInApp(isDesktopApp()), []);
return (
<div className="space-y-8">
<RemoteContentSection />
<ReadReceiptsSection />
<SenderPicturesSection />
<DesktopTelemetrySettings />
{!inApp && (
<SettingsSection id="push" title={t('push_title')}>
{/* The relay itself is chosen with advanced settings on; the link says what the page always has (settings-p2-18). */}
<SettingsLinkRow
label={t('push_link.label')}
description={t('push_link.description')}
label={tP2('label')}
description={tP2('description', { appName, page: tTabs('notifications') })}
button={t('push_link.button')}
onClick={() => goToSettings('notifications', 'push')}
/>
</SettingsSection>
)}
</div>
);
}
+12 -2
View File
@@ -84,6 +84,7 @@ import {
type SubResult,
getByPath,
matchesAllWords,
matchesSubResult,
settingsSubResults,
settingsTabLabelKey,
subResultText,
@@ -91,6 +92,8 @@ import {
tabSearchEntries,
ARCHIVE_ACTION_SEARCH_PATHS,
} from '@/lib/settings-search';
import { getDesktopBridge } from '@/lib/desktop/bridge';
import { menuBarAvailable } from '@/lib/desktop/menu-bar';
import { useAccountStore } from '@/stores/account-store';
import { useMediaQuery, atLeast, PHONE_BELOW } from '@/hooks/use-media-query';
import { useLiteLinkSegments } from '@/hooks/use-lite-link-segments';
@@ -365,6 +368,13 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
if (entry.when === 'touch') return touchScreen;
if (entry.when === 'mountainView') return mountainViewLayout;
if (entry.when === 'multiAccount') return multiAccount;
// The desktop app's own rows (settings-p2-5), and the browser's that it hides.
const desktop = getDesktopBridge();
if (entry.when === 'desktop') return !!desktop;
if (entry.when === 'desktopMac') return desktop?.platform === 'darwin';
if (entry.when === 'desktopNotMac') return !!desktop && desktop.platform !== 'darwin';
if (entry.when === 'desktopMenuBar') return menuBarAvailable();
if (entry.when === 'browser') return !desktop;
return true;
};
for (const tabId of Object.keys(tabSearchEntries) as Tab[]) {
@@ -765,7 +775,7 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
if (!trimmedQuery) return true;
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));
|| (tabSubResults[tab.id as Tab] ?? []).some((r) => matchesSubResult(r, trimmedQuery));
};
const subResultsForTab = (tabId: SettingsTabId): SubResult[] => {
@@ -774,7 +784,7 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
// 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(subResultText(r), trimmedQuery))
.filter((r) => matchesSubResult(r, trimmedQuery))
.sort((a, b) => byName(a) - byName(b))
.slice(0, 6);
};
+100 -6
View File
@@ -5,16 +5,16 @@ import {
descriptionKeyFor,
getByPath,
matchesAllWords,
matchesSubResult,
tabKeywords,
settingsSubResults,
subResultText,
tabSearchEntries,
withAppName,
} from '../settings-search';
const labelsFor = (tab: SettingsSearchTab) => settingsSubResults(en, tab, 'Bulwark').map((r) => r.label);
const resultsFor = (tab: SettingsSearchTab, query: string) =>
settingsSubResults(en, tab, 'Bulwark').filter((r) => matchesAllWords(`${r.label} ${r.description ?? ''}`, query));
settingsSubResults(en, tab, 'Bulwark').filter((r) => matchesSubResult(r, query));
describe('settings search index (English messages)', () => {
it('every entry names a label that exists, without a raw placeholder', () => {
@@ -40,7 +40,7 @@ describe('settings search index (English messages)', () => {
expect(labelsFor('notifications')).toContain('Unread count on the tab and app icon');
// 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('privacy')).toContain('Pictures from company websites');
expect(labelsFor('message_list')).toContain('Clear search when switching folders');
expect(labelsFor('privacy')).toContain('Trusted senders');
expect(labelsFor('composing')).toContain('Undo send');
@@ -66,7 +66,7 @@ describe('settings search index (English messages)', () => {
expect(resultsFor('message_list', 'inbox type').map((r) => r.label)).toEqual(['Inbox type']);
expect(resultsFor('calendar', 'import calendar').map((r) => r.label)).toContain('Import calendar');
expect(resultsFor('composing', 'mention').map((r) => r.label).slice(0, 2)).toEqual(['Mention recipients with @', 'Contacts in @ mentions']);
expect(resultsFor('security', 'two-factor').map((r) => r.label)).toContain('Two-factor authentication');
expect(resultsFor('security', 'two-factor').map((r) => r.label)).toContain('Two-step sign-in');
expect(resultsFor('filters', 'new rule').map((r) => r.label)).toEqual(['New rule']);
// "add rule" finds the Filters page itself.
expect(matchesAllWords(tabKeywords.filters, 'add rule')).toBe(true);
@@ -89,7 +89,7 @@ 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(settingsSubResults(en, 'privacy', 'Bulwark').filter((r) => r.advanced).map((r) => r.label)).not.toContain('Sender favicons');
expect(settingsSubResults(en, 'privacy', 'Bulwark').filter((r) => r.advanced).map((r) => r.label)).not.toContain('Pictures from company websites');
});
});
@@ -99,7 +99,7 @@ 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))
.filter((r) => matchesSubResult(r, query))
.map((r) => `${tab}: ${r.label}`));
it('matches whole words and the start of words, not the inside of a word', () => {
@@ -190,3 +190,97 @@ describe('{appName} in the index (#1314)', () => {
expect(withAppName({ a: 'x {appName} y', b: ['{appName}'], c: 3 }, 'N')).toEqual({ a: 'x N y', b: ['N'], c: 3 });
});
});
// settings-p2-14 (2026-11-04): words people type that found nothing or the
// wrong row, and short words that matched a dozen descriptions.
describe('settings search: more words people use', () => {
const everywhere = (query: string, shown?: (when: string | undefined) => boolean) =>
(Object.keys(tabSearchEntries) as SettingsSearchTab[]).flatMap((tab) =>
settingsSubResults(en, tab, 'Bulwark', (entry) => (shown ? shown(entry.when) : !entry.when?.startsWith('desktop')))
.filter((r) => matchesSubResult(r, query))
.map((r) => `${tab}: ${r.label}`));
it.each([
['threads', 'message_list: Group messages by conversation'],
// "Pictures from the internet" in the plain English the app shows.
['block images', 'privacy: External content'],
['dark mode', 'appearance: Light or dark'],
['timezone', 'general: Time zone'],
['reading pane', 'appearance: Mail layout'],
['week start', 'general: First day of week'],
['empty trash', 'message_list: Deleting & archiving'],
['quiet hours', 'accessibility: Do not disturb'],
])('finds "%s"', (query, row) => {
expect(everywhere(query)).toContain(row);
});
it('looks for one short word in names and synonyms only, not in descriptions', () => {
// "about" starts a word in many descriptions ("how you hear about new mail").
expect(everywhere('about')).toEqual(['about: About Bulwark']);
const row = { label: 'Sound', description: 'Plays a chime about new mail when it arrives' };
expect(matchesSubResult(row, 'about')).toBe(false);
expect(matchesSubResult(row, 'chime')).toBe(false);
expect(matchesSubResult({ ...row, keywords: 'chime bell' }, 'chime')).toBe(true);
// A longer word, or more words, still look in the description.
expect(matchesSubResult(row, 'arrives')).toBe(true);
expect(matchesSubResult(row, 'sound mail')).toBe(true);
expect(matchesSubResult(row, 'sound tray')).toBe(false);
// Chinese has no spaces: its words are looked for in descriptions too.
expect(matchesSubResult({ label: '声音', description: '新邮件时播放提示音' }, '邮件')).toBe(true);
});
});
// settings-p2-5 (2026-11-04): in the desktop app the app's own rows were
// missing from the search; "tray" found an unread count instead.
describe('settings search in the desktop app', () => {
const inApp = (when: string | undefined, mac = false) => {
if (when === 'browser') return false;
if (when === 'desktopMac') return mac;
if (when === 'desktopNotMac' || when === 'desktopMenuBar') return !mac;
return when !== 'touch' && when !== 'mountainView' && when !== 'multiAccount';
};
const inAppResults = (query: string, mac = false) => (Object.keys(tabSearchEntries) as SettingsSearchTab[]).flatMap((tab) =>
settingsSubResults(en, tab, 'Bulwark', (entry) => inApp(entry.when, mac))
.filter((r) => matchesSubResult(r, query))
.map((r) => `${tab}: ${r.label}`));
const inBrowser = (query: string) => (Object.keys(tabSearchEntries) as SettingsSearchTab[]).flatMap((tab) =>
settingsSubResults(en, tab, 'Bulwark', (entry) => !entry.when?.startsWith('desktop'))
.filter((r) => matchesSubResult(r, query))
.map((r) => `${tab}: ${r.label}`));
it.each([
['tray', 'general: Keep running in the tray when closed'],
['quiet hours', 'notifications: Quiet hours'],
['do not disturb', 'notifications: Quiet hours'],
['autostart', 'general: Start when you sign in to your computer'],
['start with windows', 'general: Start when you sign in to your computer'],
['startup', 'general: Start when you sign in to your computer'],
['menu bar', 'general: Always show the menu bar'],
['taskbar', 'notifications: Flash the taskbar on new mail'],
['developer tools', 'general: Troubleshooting'],
['updates', 'general: Updates'],
['usage', 'privacy: Send usage info'],
['crash', 'privacy: Send crash reports'],
['default mail app', 'general: Default mail app'],
])('finds "%s"', (query, row) => {
expect(inAppResults(query)).toContain(row);
});
it('lists "tray" first for the tray row, and the Dock row on a Mac', () => {
expect(inAppResults('tray')[0]).toBe('general: Keep running in the tray when closed');
expect(inAppResults('taskbar', true)).toContain('notifications: Bounce the Dock icon on new mail');
expect(inAppResults('menu bar', true)).not.toContain('general: Always show the menu bar');
});
it('marks developer tools as an advanced row', () => {
const devTools = settingsSubResults(en, 'general', 'Bulwark').find((r) => r.label === 'Troubleshooting');
expect(devTools?.advanced).toBe(true);
});
it('keeps the app rows out of a browser, and the browser push rows out of the app', () => {
expect(inBrowser('tray')).not.toContain('general: Keep running in the tray when closed');
expect(inBrowser('quiet hours')).not.toContain('notifications: Quiet hours');
expect(inAppResults('relay')).toEqual([]);
expect(inBrowser('relay')).toContain('privacy: Relay for notifications');
});
});
+75 -19
View File
@@ -35,7 +35,17 @@ export type SettingsSearchCondition =
/** The Mountain View interface layout. */
| 'mountainView'
/** More than one account signed in. */
| 'multiAccount';
| 'multiAccount'
/** In the desktop app (its bridge is there). */
| 'desktop'
/** In the desktop app on Windows or Linux. */
| 'desktopNotMac'
/** In the desktop app on macOS. */
| 'desktopMac'
/** In the desktop app where its own menu bar can be pinned (not macOS). */
| 'desktopMenuBar'
/** In a browser, not the desktop app. */
| 'browser';
export interface SettingsSearchEntry {
/**
@@ -57,6 +67,8 @@ export interface SettingsSearchEntry {
/** Key of a row's synonyms in the locale files. */
const syn = (id: string) => `uxs_structure.search.${id}`;
/** Synonyms added in the second settings pass. */
const syn2 = (id: string) => `uxp2_settings.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
@@ -81,17 +93,29 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
{ label: 'settings.language_region.date_format.label' },
{ label: 'settings.language_region.date_locale.label', advanced: true },
{ label: 'settings.language_region.time_format.label' },
{ label: 'settings.language_region.time_zone.label' },
{ label: 'settings.language_region.first_day.label' },
{ label: 'settings.language_region.time_zone.label', synonyms: syn2('timezone') },
{ label: 'settings.language_region.first_day.label', synonyms: syn2('week_start') },
{ label: 'appLock.section_title' },
{ label: 'appLock.enable_label', synonyms: syn('app_lock') },
{ label: 'protocol_handlers.title' },
{ label: 'protocol_handlers.mailto_label' },
{ label: 'protocol_handlers.webcal_label' },
{ label: 'protocol_handlers.protocol_open_mode_label', advanced: true },
{ label: 'desktopOs.section_title', when: 'desktop' },
{ label: 'desktopOs.close_to_tray_label', when: 'desktop', synonyms: syn2('tray') },
{ label: 'desktopOs.start_at_login_label', when: 'desktop', synonyms: syn2('autostart') },
{ label: 'desktopOs.show_shortcut_label', when: 'desktop' },
{ label: 'desktopOs.default_app_label', when: 'desktop', synonyms: syn2('default_app') },
{ label: 'protocol_handlers.title', when: 'browser' },
{ label: 'protocol_handlers.mailto_label', when: 'browser' },
{ label: 'protocol_handlers.webcal_label', when: 'browser' },
{ label: 'protocol_handlers.protocol_open_mode_label', advanced: true, when: 'browser' },
{ label: 'desktopUi.settings.title', when: 'desktop' },
{ label: 'desktopUi.settings.double_click_label', when: 'desktop' },
{ label: 'desktopUi.settings.restore_label', when: 'desktop' },
{ label: 'desktopMenuBar.always_show_label', when: 'desktopMenuBar', synonyms: syn2('menu_bar') },
{ label: 'tour.settings_section' },
{ label: 'tour.restart_title' },
{ label: 'tour.show_on_new_devices_title', advanced: true },
{ label: 'desktop_updates.title', when: 'desktop', synonyms: syn2('updates') },
{ label: 'desktop_updates.channel_label', when: 'desktop' },
{ label: 'desktopDevTools.section_title', when: 'desktop', advanced: true, synonyms: syn2('devtools') },
],
notifications: [
{ label: 'settings.notifications.email.title' },
@@ -103,18 +127,23 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
{ label: 'settings.notifications.calendar.enabled' },
{ label: 'settings.notifications.sound_selection.title' },
{ label: 'settings.notifications.sound_selection.choose' },
{ label: 'settings.notifications.push.title' },
{ label: 'settings.notifications.push.relay_label', advanced: true },
{ label: 'uxp2_settings.desktop_notifications.title', when: 'desktop' },
{ label: 'desktopOs.attention_label', when: 'desktopNotMac', synonyms: syn2('taskbar') },
{ label: 'desktopOs.attention_mac_label', when: 'desktopMac', synonyms: syn2('taskbar') },
{ label: 'desktopOs.quiet_hours_label', when: 'desktop', synonyms: syn2('quiet_hours') },
{ label: 'uxp2_settings.desktop_notifications.closed_label', when: 'desktop' },
{ label: 'settings.notifications.push.title', when: 'browser' },
{ label: 'settings.notifications.push.relay_label', advanced: true, when: 'browser' },
{ label: 'settings.notifications.push.devices_title', advanced: true },
],
appearance: [
{ label: 'settings.appearance.theme.label' },
{ label: 'settings.appearance.theme.label', synonyms: syn2('dark_mode') },
{ label: 'settings.themes.title' },
{ label: 'settings.appearance.sections.text' },
{ label: 'settings.appearance.list_density.label' },
{ label: 'settings.appearance.sections.layout' },
{ label: 'settings.appearance.interface_layout.label' },
{ label: 'settings.email_behavior.mail_layout.label' },
{ label: 'settings.email_behavior.mail_layout.label', synonyms: syn2('reading_pane') },
{ label: 'settings.appearance.toolbar_position.label' },
{ label: 'settings.appearance.toolbar_labels.label' },
{ label: 'settings.appearance.sections.sidebar' },
@@ -177,7 +206,7 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
{ label: 'settings.accessibility.sections.alerts' },
{ label: 'settings.accessibility.visual_alerts.label' },
{ label: 'settings.accessibility.unread_counts.label' },
{ label: 'settings.accessibility.do_not_disturb.label' },
{ label: 'settings.accessibility.do_not_disturb.label', synonyms: syn2('do_not_disturb') },
{ label: 'settings.accessibility.sections.screen_readers' },
{ label: 'settings.accessibility.announce_new_mail.label' },
{ label: 'settings.accessibility.announce_preview.label' },
@@ -186,7 +215,7 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
{ label: 'settings.accessibility.reset.label' },
],
message_list: [
{ label: 'settings.email_behavior.group_conversations.label' },
{ label: 'settings.email_behavior.group_conversations.label', synonyms: syn2('threads') },
{ label: 'settings.email_behavior.show_preview.label' },
{ label: 'settings.appearance.message_list_order.label' },
{ label: 'settings.appearance.message_list_order.scope.label' },
@@ -194,7 +223,7 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
{ label: 'settings.email_behavior.show_verification_codes.label' },
{ label: 'settings.message_list.sections.actions' },
{ label: 'settings.email_behavior.hover_actions.label' },
{ label: 'settings.message_list.sections.delete' },
{ label: 'settings.message_list.sections.delete', synonyms: syn2('empty_trash') },
{ label: 'settings.email_behavior.delete_action.label' },
{ label: 'settings.email_behavior.archive_mode.label' },
{ label: 'settings.email_behavior.confirm_bulk_delete.label' },
@@ -286,7 +315,6 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
{ label: 'settings.security.sections.devices' },
{ 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 },
{ label: 'settings.security.encryption.section_title', advanced: true },
@@ -352,18 +380,21 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
],
privacy: [
{ label: 'settings.reading_page.sections.images', synonyms: syn('remote_content') },
{ label: 'settings.email_behavior.external_content.label' },
{ label: 'settings.email_behavior.external_content.label', synonyms: syn2('block_images') },
{ 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: 'uxp2_settings.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') },
{ label: 'desktopTelemetry.section_title', when: 'desktop' },
{ label: 'desktopTelemetry.telemetry_label', when: 'desktop', synonyms: syn2('telemetry') },
{ label: 'desktopTelemetry.crash_label', when: 'desktop', synonyms: syn2('crash') },
{ label: 'uxs_structure.privacy.push_title', when: 'browser' },
{ label: 'uxp2_settings.privacy_push.label', when: 'browser', synonyms: syn('push') },
],
backup: [
{ label: 'settings.advanced.sections.backup' },
@@ -521,6 +552,31 @@ export function subResultText(result: SubResult): string {
return [result.label, result.description ?? '', result.keywords ?? ''].join(' ');
}
/**
* Shortest one-word query that is looked for in a row's description too.
* A short word on its own ("about", "mail", "tab") starts a word in a dozen
* descriptions, which buried the row named by it (settings-p2-14). With more
* words the others narrow it down, so "reply signature" still finds the row
* whose description talks about replies.
*/
export const DESCRIPTION_MIN_WORD = 6;
/**
* A row matches when every word of the query starts a word of its name,
* description or synonyms; a single word of up to five letters only in its
* name and synonyms. Chinese, Japanese and Thai words are short by nature
* and always look in the description too.
*/
export function matchesSubResult(result: SubResult, query: string): boolean {
const words = searchWords(query);
if (words.length === 0) return false;
const [only] = words;
if (words.length === 1 && only.length < DESCRIPTION_MIN_WORD && !NO_WORD_BREAKS.test(only)) {
return matchesAllWords(`${result.label} ${result.keywords ?? ''}`, only);
}
return matchesAllWords(subResultText(result), query);
}
export function getByPath(obj: unknown, path: string): unknown {
let cur: unknown = obj;
for (const key of path.split('.')) {
+42
View File
@@ -3505,6 +3505,48 @@
"description": "تسجيل الخروج من هذا الحساب على هذا الجهاز."
}
},
"uxp2_settings": {
"search": {
"tray": "شريط النظام منطقة الإشعارات الخلفية التصغير إلى شريط النظام tray",
"autostart": "التشغيل التلقائي بدء التشغيل التشغيل مع Windows عند تسجيل الدخول",
"default_app": "تطبيق البريد الافتراضي برنامج البريد الافتراضي mailto روابط البريد",
"menu_bar": "شريط القوائم القوائم مفتاح Alt ملف تحرير عرض",
"updates": "التحديثات تحديث إصدار جديد بيتا قناة",
"devtools": "أدوات المطور وحدة التحكم فحص F12 تصحيح الأخطاء devtools",
"taskbar": "شريط المهام وميض تنبيه Dock",
"quiet_hours": "ساعات الهدوء عدم الإزعاج صامت كتم ليلي",
"telemetry": "معلومات الاستخدام القياس عن بعد إحصاءات تحليلات",
"crash": "تقارير الأعطال تقارير الأخطاء تعطل",
"threads": "سلاسل المحادثات محادثة سلسلة threads",
"block_images": "حظر الصور الصور الخارجية تحميل الصور",
"dark_mode": "الوضع الداكن الوضع الفاتح الوضع الليلي dark mode",
"timezone": "المنطقة الزمنية التوقيت timezone",
"reading_pane": "جزء القراءة جزء المعاينة عرض مقسم",
"week_start": "بداية الأسبوع أول يوم الاثنين الأحد السبت",
"empty_trash": "إفراغ سلة المهملات المهملات العناصر المحذوفة",
"do_not_disturb": "ساعات الهدوء كتم صمت"
},
"desktop_notifications": {
"title": "على هذا الكمبيوتر",
"closed_label": "عند إغلاق النافذة",
"closed_description": "يخبرك {appName} بالبريد الجديد ما دام يعمل. ليبقى يعمل في شريط النظام بعد إغلاق نافذته، فعّل ذلك في {page}."
},
"push_unsupported": "لا يستطيع هذا المتصفح عرض الإشعارات عندما يكون الموقع مغلقًا.",
"backup_description_app": "استخدم الإعدادات نفسها على أجهزتك الأخرى.",
"sender_favicons": {
"label": "صور من مواقع الشركات",
"description": "إظهار أيقونات المواقع كصور شخصية للمرسلين من الشركات"
},
"privacy_push": {
"label": "مرحّل الإشعارات",
"description": "تمر الإشعارات أثناء إغلاق {appName} عبر مرحّل دفع لا يرى بريدك أبدًا. تشغّلها أو توقفها في صفحة {page}."
},
"reset": {
"message": "أعد جميع الإعدادات إلى ما كانت عليه في البداية. يبقى بريدك وعوامل التصفية والرد التلقائي والتواقيع كما هي.",
"sync_on": "تستخدم أجهزتك الأخرى الإعدادات نفسها، لذا ستتغير هي أيضًا.",
"sync_off": "تحتفظ أجهزتك الأخرى بإعداداتها."
}
},
"setup": {
"title": "إعداد Bulwark Webmail",
"subtitle": "اضبط نسخة البريد الإلكتروني على الويب من المتصفح.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Tanca la sessió d’aquest compte en aquest dispositiu."
}
},
"uxp2_settings": {
"search": {
"tray": "safata safata del sistema àrea de notificació segon pla minimitzar",
"autostart": "inici automàtic arrencada iniciar amb Windows en iniciar la sessió",
"default_app": "aplicació de correu predeterminada programa de correu mailto enllaços de correu",
"menu_bar": "barra de menús menús tecla Alt Fitxer Edita Visualitza",
"updates": "actualitzacions actualitzar versió nova beta canal",
"devtools": "eines de desenvolupament devtools consola inspeccionar F12 depuració",
"taskbar": "barra de tasques parpelleig Dock atenció",
"quiet_hours": "hores de silenci no molestis silenci mut nit",
"telemetry": "informació d’ús telemetria estadístiques analítica",
"crash": "informes d’error informes de fallada fallada",
"threads": "fils converses conversa agrupar threads",
"block_images": "bloquejar imatges imatges externes carregar imatges",
"dark_mode": "mode fosc mode clar mode nocturn tema fosc",
"timezone": "fus horari zona horària timezone",
"reading_pane": "panell de lectura panell de previsualització vista dividida",
"week_start": "inici de la setmana primer dia dilluns diumenge",
"empty_trash": "buidar la paperera paperera elements suprimits",
"do_not_disturb": "hores de silenci silenci mut"
},
"desktop_notifications": {
"title": "En aquest ordinador",
"closed_label": "Amb la finestra tancada",
"closed_description": "{appName} us avisa del correu nou mentre s’executa. Perquè continuï a la safata quan tanqueu la finestra, activeu-ho a {page}."
},
"push_unsupported": "Aquest navegador no pot mostrar notificacions quan el lloc web està tancat.",
"backup_description_app": "Feu servir la mateixa configuració als altres dispositius.",
"sender_favicons": {
"label": "Imatges dels llocs web de les empreses",
"description": "Mostra icones de llocs web com a imatges de perfil per als remitents d'empresa"
},
"privacy_push": {
"label": "Repetidor de notificacions",
"description": "Les notificacions amb {appName} tancat passen per un repetidor push, que mai no veu el vostre correu. Les activeu o desactiveu a la pàgina {page}."
},
"reset": {
"message": "Torna tota la configuració a com era al principi. El correu, els filtres, la resposta automàtica i les signatures es queden com estan.",
"sync_on": "Els altres dispositius fan servir la mateixa configuració, així que també canvien.",
"sync_off": "Els altres dispositius mantenen la seva configuració."
}
},
"setup": {
"title": "Configuració de Bulwark Webmail",
"subtitle": "Configureu la vostra instància de correu web des del navegador.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Odhlásit tento účet na tomto zařízení."
}
},
"uxp2_settings": {
"search": {
"tray": "oznamovací oblast systémová lišta tray na pozadí minimalizovat",
"autostart": "automatické spuštění po startu spustit s Windows při přihlášení",
"default_app": "výchozí poštovní aplikace výchozí e-mailový program mailto odkazy na e-mail",
"menu_bar": "panel nabídek nabídka klávesa Alt Soubor Úpravy Zobrazení",
"updates": "aktualizace nová verze beta kanál",
"devtools": "nástroje pro vývojáře devtools konzole prozkoumat F12 ladění",
"taskbar": "hlavní panel blikání Dock pozornost",
"quiet_hours": "tiché hodiny nerušit ztlumit noc",
"telemetry": "údaje o používání telemetrie statistiky analytika",
"crash": "hlášení o pádu chybová hlášení pád",
"threads": "vlákna konverzace seskupit threads",
"block_images": "blokovat obrázky externí obrázky načítat obrázky",
"dark_mode": "tmavý režim světlý režim noční režim dark mode",
"timezone": "časové pásmo timezone",
"reading_pane": "podokno čtení náhled rozdělené zobrazení",
"week_start": "začátek týdne první den pondělí neděle",
"empty_trash": "vysypat koš koš smazané položky",
"do_not_disturb": "tiché hodiny ztlumit ticho"
},
"desktop_notifications": {
"title": "Na tomto počítači",
"closed_label": "Při zavřeném okně",
"closed_description": "{appName} vás upozorní na novou poštu, dokud běží. Aby po zavření okna běžel dál v oznamovací oblasti, zapněte to v části {page}."
},
"push_unsupported": "Tento prohlížeč nemůže zobrazovat oznámení, když je web zavřený.",
"backup_description_app": "Používejte stejná nastavení na svých dalších zařízeních.",
"sender_favicons": {
"label": "Obrázky z webů firem",
"description": "Zobrazovat ikony webových stránek jako profilové obrázky firemních odesílatelů"
},
"privacy_push": {
"label": "Relay pro oznámení",
"description": "Oznámení při zavřeném {appName} jdou přes push relay, který nikdy nevidí vaši poštu. Zapínáte a vypínáte je na stránce {page}."
},
"reset": {
"message": "Vrátí všechna nastavení do původního stavu. Vaše pošta, filtry, automatická odpověď a podpisy zůstanou, jak jsou.",
"sync_on": "Vaše další zařízení používají stejná nastavení, takže se změní také.",
"sync_off": "Vaše další zařízení si nastavení ponechají."
}
},
"setup": {
"title": "Nastavení Bulwark Webmail",
"subtitle": "Nastavte svou instanci webmailu v prohlížeči.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Log ud af denne konto på denne enhed."
}
},
"uxp2_settings": {
"search": {
"tray": "systembakke meddelelsesområde baggrund minimer til bakke tray",
"autostart": "autostart opstart start med Windows ved login",
"default_app": "standardmailapp standard e-mailprogram mailto mail-links",
"menu_bar": "menulinje menuer Alt-tast Filer Rediger Vis",
"updates": "opdateringer opdatering ny version beta kanal",
"devtools": "udviklerværktøjer devtools konsol inspicer F12 fejlfinding",
"taskbar": "proceslinje blink Dock opmærksomhed",
"quiet_hours": "stille timer forstyr ikke lydløs nat",
"telemetry": "brugsdata telemetri statistik analyse",
"crash": "nedbrudsrapporter fejlrapporter nedbrud",
"threads": "tråde samtaler samtalevisning threads",
"block_images": "bloker billeder eksterne billeder indlæs billeder",
"dark_mode": "mørk tilstand lys tilstand nattilstand dark mode",
"timezone": "tidszone timezone",
"reading_pane": "læserude eksempelrude opdelt visning",
"week_start": "ugestart første ugedag mandag søndag",
"empty_trash": "tøm papirkurven papirkurv slettede elementer",
"do_not_disturb": "stille timer lydløs stilhed"
},
"desktop_notifications": {
"title": "På denne computer",
"closed_label": "Når vinduet er lukket",
"closed_description": "{appName} giver besked om ny mail, så længe den kører. Slå det til under {page}, hvis den skal køre videre i systembakken, når du lukker vinduet."
},
"push_unsupported": "Denne browser kan ikke vise notifikationer, når websiden er lukket.",
"backup_description_app": "Brug de samme indstillinger på dine andre enheder.",
"sender_favicons": {
"label": "Billeder fra virksomheders websteder",
"description": "Vis hjemmesideikoner som profilbilleder for forretningsafsendere"
},
"privacy_push": {
"label": "Relæ til notifikationer",
"description": "Notifikationer mens {appName} er lukket går gennem et push-relæ, som aldrig ser din mail. Du slår dem til og fra på siden {page}."
},
"reset": {
"message": "Sæt alle indstillinger tilbage til, hvordan de var fra start. Din mail, dine filtre, dit autosvar og dine signaturer forbliver, som de er.",
"sync_on": "Dine andre enheder bruger de samme indstillinger, så de ændres også.",
"sync_off": "Dine andre enheder beholder deres indstillinger."
}
},
"setup": {
"title": "Opsætning af Bulwark Webmail",
"subtitle": "Opsæt din webmailinstans i browseren.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Von diesem Konto auf diesem Gerät abmelden."
}
},
"uxp2_settings": {
"search": {
"tray": "Infobereich Taskleiste Tray Hintergrund im Hintergrund weiterlaufen minimieren",
"autostart": "Autostart Systemstart mit Windows starten beim Anmelden starten automatisch starten",
"default_app": "Standard-Mail-App Standard-E-Mail-Programm mailto E-Mail-Links Standardprogramm",
"menu_bar": "Menüleiste Menü Alt-Taste Datei Bearbeiten Ansicht",
"updates": "Updates Aktualisierung neue Version Beta Kanal",
"devtools": "Entwicklertools Devtools Konsole untersuchen F12 Fehlersuche",
"taskbar": "Taskleiste blinken aufblinken Dock hüpfen Aufmerksamkeit",
"quiet_hours": "Ruhezeiten nicht stören stumm lautlos Nacht",
"telemetry": "Nutzungsdaten Telemetrie Statistik Analyse",
"crash": "Absturzberichte Fehlerberichte Absturz",
"threads": "Threads Unterhaltungen Konversation gruppieren Verlauf",
"block_images": "Bilder blockieren externe Bilder Bilder laden",
"dark_mode": "Dunkelmodus dunkler Modus heller Modus Nachtmodus dark mode",
"timezone": "Zeitzone timezone",
"reading_pane": "Lesebereich Vorschaufenster Vorschau geteilte Ansicht",
"week_start": "Wochenbeginn Wochenanfang Montag Sonntag",
"empty_trash": "Papierkorb leeren Papierkorb gelöschte Elemente",
"do_not_disturb": "Ruhezeiten stumm Stille"
},
"desktop_notifications": {
"title": "Auf diesem Computer",
"closed_label": "Bei geschlossenem Fenster",
"closed_description": "{appName} meldet neue E-Mails, solange es läuft. Damit es nach dem Schließen des Fensters im Infobereich weiterläuft, schalten Sie das unter {page} ein."
},
"push_unsupported": "Dieser Browser kann keine Benachrichtigungen zeigen, wenn die Website geschlossen ist.",
"backup_description_app": "Dieselben Einstellungen auf Ihren anderen Geräten nutzen.",
"sender_favicons": {
"label": "Bilder von Firmen-Websites",
"description": "Bei Firmen das Symbol ihrer Website als Bild des Absenders zeigen"
},
"privacy_push": {
"label": "Relay für Benachrichtigungen",
"description": "Benachrichtigungen bei geschlossenem {appName} laufen über ein Push-Relay, das Ihre E-Mails nie sieht. Sie schalten sie auf der Seite {page} ein und aus."
},
"reset": {
"message": "Alle Einstellungen auf den Anfangszustand zurücksetzen. Ihre E-Mails, Filter, Abwesenheitsnotiz und Signaturen bleiben, wie sie sind.",
"sync_on": "Ihre anderen Geräte nutzen dieselben Einstellungen, also ändern sie sich auch.",
"sync_off": "Ihre anderen Geräte behalten ihre Einstellungen."
}
},
"setup": {
"title": "Einrichtung von Bulwark Webmail",
"subtitle": "Richten Sie Ihre Webmail-Instanz im Browser ein.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Sign out of this account on this device."
}
},
"uxp2_settings": {
"search": {
"tray": "tray system tray notification area background close to tray minimize to tray",
"autostart": "autostart startup start with Windows launch at login open at login run at startup",
"default_app": "default mail app default email program mailto email links",
"menu_bar": "menu bar menus Alt key File Edit View",
"updates": "updates update new version beta channel",
"devtools": "developer tools devtools console inspect F12 debugging",
"taskbar": "taskbar flash blink Dock bounce attention",
"quiet_hours": "quiet hours do not disturb silent mute night",
"telemetry": "usage info telemetry statistics analytics",
"crash": "crash reports error reports crash dump",
"threads": "threads thread conversation view conversations",
"block_images": "block images remote images load images external images",
"dark_mode": "dark mode light mode night mode dark theme",
"timezone": "timezone time zone",
"reading_pane": "reading pane preview pane split view",
"week_start": "week start start of week Monday Sunday",
"empty_trash": "empty trash trash bin deleted items",
"do_not_disturb": "quiet hours mute silence"
},
"desktop_notifications": {
"title": "On this computer",
"closed_label": "With the window closed",
"closed_description": "{appName} tells you about new mail as long as it runs. To keep it running in the tray after you close its window, turn that on in {page}."
},
"push_unsupported": "This browser can't show notifications when the website is closed.",
"backup_description_app": "Use the same settings on your other devices.",
"sender_favicons": {
"label": "Pictures from company websites",
"description": "For companies, show the icon of their website as the sender's picture"
},
"privacy_push": {
"label": "Relay for notifications",
"description": "Notifications while {appName} is closed go through a push relay, which never sees your mail. You turn them on or off on the {page} page."
},
"reset": {
"message": "Put all settings back to how they were at the start. Your mail, filters, out of office and signatures stay as they are.",
"sync_on": "Your other devices use the same settings, so they change too.",
"sync_off": "Your other devices keep their settings."
}
},
"setup": {
"title": "Bulwark Webmail setup",
"subtitle": "Configure your webmail instance from the browser.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Cerrar sesión de esta cuenta en este dispositivo."
}
},
"uxp2_settings": {
"search": {
"tray": "bandeja bandeja del sistema área de notificación segundo plano minimizar",
"autostart": "inicio automático arranque iniciar con Windows al iniciar sesión",
"default_app": "aplicación de correo predeterminada programa de correo mailto enlaces de correo",
"menu_bar": "barra de menús menús tecla Alt Archivo Editar Ver",
"updates": "actualizaciones actualizar nueva versión beta canal",
"devtools": "herramientas de desarrollo devtools consola inspeccionar F12 depuración",
"taskbar": "barra de tareas parpadear Dock atención",
"quiet_hours": "horas de silencio no molestar silencio silenciar noche",
"telemetry": "información de uso telemetría estadísticas analítica",
"crash": "informes de errores informes de fallos fallo",
"threads": "hilos conversaciones agrupar threads",
"block_images": "bloquear imágenes imágenes externas cargar imágenes",
"dark_mode": "modo oscuro modo claro modo nocturno tema oscuro",
"timezone": "zona horaria huso horario timezone",
"reading_pane": "panel de lectura panel de vista previa vista dividida",
"week_start": "inicio de semana primer día lunes domingo",
"empty_trash": "vaciar la papelera papelera elementos eliminados",
"do_not_disturb": "horas de silencio silenciar silencio"
},
"desktop_notifications": {
"title": "En este ordenador",
"closed_label": "Con la ventana cerrada",
"closed_description": "{appName} le avisa del correo nuevo mientras está abierto. Para que siga en la bandeja al cerrar la ventana, actívelo en {page}."
},
"push_unsupported": "Este navegador no puede mostrar notificaciones cuando el sitio web está cerrado.",
"backup_description_app": "Use los mismos ajustes en sus otros dispositivos.",
"sender_favicons": {
"label": "Imágenes de los sitios web de empresas",
"description": "Mostrar iconos de sitios web como fotos de perfil para remitentes empresariales"
},
"privacy_push": {
"label": "Relé de notificaciones",
"description": "Las notificaciones con {appName} cerrado pasan por un relé push, que nunca ve su correo. Las activa o desactiva en la página {page}."
},
"reset": {
"message": "Devuelve todos los ajustes a como estaban al principio. Su correo, filtros, respuesta automática y firmas no cambian.",
"sync_on": "Sus otros dispositivos usan los mismos ajustes, así que también cambian.",
"sync_off": "Sus otros dispositivos conservan sus ajustes."
}
},
"setup": {
"title": "Configuración de Bulwark Webmail",
"subtitle": "Configura tu instancia de webmail desde el navegador.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Logi sellest kontost selles seadmes välja."
}
},
"uxp2_settings": {
"search": {
"tray": "salv süsteemisalv teavitusala taust minimeeri salve tray",
"autostart": "automaatne käivitus käivitus käivita koos Windowsiga sisselogimisel",
"default_app": "vaikimisi meilirakendus vaikimisi e-posti programm mailto meililingid",
"menu_bar": "menüüriba menüüd Alt-klahv Fail Redigeeri Vaade",
"updates": "uuendused uuendus uus versioon beeta kanal",
"devtools": "arendustööriistad devtools konsool uuri F12 silumine",
"taskbar": "tegumiriba vilkumine Dock tähelepanu",
"quiet_hours": "vaikne aeg ära sega vaigista öö",
"telemetry": "kasutusandmed telemeetria statistika analüütika",
"crash": "krahhiaruanded vearaportid krahh",
"threads": "lõimed vestlused rühmita threads",
"block_images": "blokeeri pildid välised pildid laadi pildid",
"dark_mode": "tume režiim hele režiim öörežiim dark mode",
"timezone": "ajavöönd timezone",
"reading_pane": "lugemispaan eelvaatepaan jagatud vaade",
"week_start": "nädala algus esimene päev esmaspäev pühapäev",
"empty_trash": "tühjenda prügikast prügikast kustutatud üksused",
"do_not_disturb": "vaikne aeg vaigista vaikus"
},
"desktop_notifications": {
"title": "Selles arvutis",
"closed_label": "Suletud aknaga",
"closed_description": "{appName} annab uutest kirjadest teada, kuni see töötab. Et see pärast akna sulgemist salves edasi töötaks, lülita see sisse jaotises {page}."
},
"push_unsupported": "See brauser ei saa teavitusi näidata, kui veebileht on suletud.",
"backup_description_app": "Kasuta samu seadeid oma teistes seadmetes.",
"sender_favicons": {
"label": "Pildid ettevõtete veebisaitidelt",
"description": "Näita äriliste saatjate profiilipiltidena veebisaitide ikoone"
},
"privacy_push": {
"label": "Teavituste relee",
"description": "Teavitused ajal, mil {appName} on suletud, käivad läbi push-relee, mis ei näe kunagi sinu kirju. Lülitad need sisse või välja lehel {page}."
},
"reset": {
"message": "Taastab kõik seaded algsel kujul. Sinu kirjad, filtrid, automaatvastus ja allkirjad jäävad samaks.",
"sync_on": "Sinu teised seadmed kasutavad samu seadeid, seega muutuvad ka need.",
"sync_off": "Sinu teised seadmed jätavad oma seaded alles."
}
},
"setup": {
"title": "Bulwark Webmaili seadistamine",
"subtitle": "Seadista oma veebipostiteenus brauseris.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "خروج از این حساب در این دستگاه."
}
},
"uxp2_settings": {
"search": {
"tray": "سینی سیستم ناحیه اعلان پس‌زمینه کوچک کردن به سینی tray",
"autostart": "اجرای خودکار راه‌اندازی شروع با Windows هنگام ورود",
"default_app": "برنامه ایمیل پیش‌فرض برنامه پیش‌فرض ایمیل mailto پیوندهای ایمیل",
"menu_bar": "نوار منو منوها کلید Alt پرونده ویرایش نما",
"updates": "به‌روزرسانی‌ها به‌روزرسانی نسخه جدید بتا کانال",
"devtools": "ابزارهای توسعه‌دهنده کنسول بازرسی F12 اشکال‌زدایی devtools",
"taskbar": "نوار وظیفه چشمک Dock توجه",
"quiet_hours": "ساعات سکوت مزاحم نشوید بی‌صدا شب",
"telemetry": "اطلاعات استفاده تله‌متری آمار تحلیل",
"crash": "گزارش خرابی گزارش خطا خرابی",
"threads": "رشته‌ها گفتگوها گروه‌بندی threads",
"block_images": "مسدود کردن تصاویر تصاویر خارجی بارگذاری تصاویر",
"dark_mode": "حالت تیره حالت روشن حالت شب dark mode",
"timezone": "منطقه زمانی timezone",
"reading_pane": "پنل خواندن پنل پیش‌نمایش نمای تقسیم‌شده",
"week_start": "شروع هفته اولین روز شنبه دوشنبه یکشنبه",
"empty_trash": "خالی کردن سطل زباله سطل زباله موارد حذف‌شده",
"do_not_disturb": "ساعات سکوت بی‌صدا سکوت"
},
"desktop_notifications": {
"title": "روی این رایانه",
"closed_label": "وقتی پنجره بسته است",
"closed_description": "{appName} تا وقتی اجرا می‌شود از ایمیل‌های تازه خبرتان می‌کند. برای اینکه پس از بستن پنجره در سینی سیستم اجرا بماند، آن را در {page} روشن کنید."
},
"push_unsupported": "این مرورگر وقتی وب‌سایت بسته است نمی‌تواند اعلان نشان دهد.",
"backup_description_app": "از همین تنظیمات در دستگاه‌های دیگر خود استفاده کنید.",
"sender_favicons": {
"label": "تصاویر از وب‌سایت شرکت‌ها",
"description": "نمایش آیکون وبسایت به عنوان تصویر پروفایل فرستندگان تجاری"
},
"privacy_push": {
"label": "رله اعلان‌ها",
"description": "اعلان‌ها وقتی {appName} بسته است از یک رله پوش می‌گذرند که هرگز ایمیل شما را نمی‌بیند. آن‌ها را در صفحه {page} روشن یا خاموش می‌کنید."
},
"reset": {
"message": "همه تنظیمات را به حالت اول برمی‌گرداند. ایمیل‌ها، فیلترها، پاسخ خودکار و امضاهای شما همان‌طور می‌مانند.",
"sync_on": "دستگاه‌های دیگر شما از همین تنظیمات استفاده می‌کنند، پس آن‌ها هم تغییر می‌کنند.",
"sync_off": "دستگاه‌های دیگر شما تنظیمات خود را نگه می‌دارند."
}
},
"setup": {
"title": "راه‌اندازی Bulwark Webmail",
"subtitle": "نمونهٔ وب‌میل خود را از مرورگر پیکربندی کنید.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Se déconnecter de ce compte sur cet appareil."
}
},
"uxp2_settings": {
"search": {
"tray": "zone de notification barre système arrière-plan réduire tray",
"autostart": "démarrage automatique lancement démarrer avec Windows à l’ouverture de session",
"default_app": "application de messagerie par défaut programme de messagerie mailto liens e-mail",
"menu_bar": "barre de menus menus touche Alt Fichier Édition Affichage",
"updates": "mises à jour mise à jour nouvelle version bêta canal",
"devtools": "outils de développement devtools console inspecter F12 débogage",
"taskbar": "barre des tâches clignoter Dock attention",
"quiet_hours": "heures calmes ne pas déranger silencieux muet nuit",
"telemetry": "données d’utilisation télémétrie statistiques analyse",
"crash": "rapports de plantage rapports d’erreur plantage",
"threads": "fils conversations regrouper threads",
"block_images": "bloquer les images images externes charger les images",
"dark_mode": "mode sombre mode clair mode nuit thème sombre",
"timezone": "fuseau horaire timezone",
"reading_pane": "volet de lecture volet d’aperçu vue partagée",
"week_start": "début de semaine premier jour lundi dimanche",
"empty_trash": "vider la corbeille corbeille éléments supprimés",
"do_not_disturb": "heures calmes muet silence"
},
"desktop_notifications": {
"title": "Sur cet ordinateur",
"closed_label": "Fenêtre fermée",
"closed_description": "{appName} vous prévient des nouveaux e-mails tant qu’il fonctionne. Pour qu’il reste dans la zone de notification une fois la fenêtre fermée, activez-le dans {page}."
},
"push_unsupported": "Ce navigateur ne peut pas afficher de notifications quand le site est fermé.",
"backup_description_app": "Utilisez les mêmes paramètres sur vos autres appareils.",
"sender_favicons": {
"label": "Images des sites web des entreprises",
"description": "Afficher les icônes de sites web comme photos de profil pour les expéditeurs professionnels"
},
"privacy_push": {
"label": "Relais des notifications",
"description": "Les notifications reçues quand {appName} est fermé passent par un relais push, qui ne voit jamais vos e-mails. Vous les activez ou désactivez sur la page {page}."
},
"reset": {
"message": "Remet tous les paramètres comme au départ. Vos e-mails, filtres, réponse automatique et signatures restent tels quels.",
"sync_on": "Vos autres appareils utilisent les mêmes paramètres, ils changent donc aussi.",
"sync_off": "Vos autres appareils gardent leurs paramètres."
}
},
"setup": {
"title": "Configuration de Bulwark Webmail",
"subtitle": "Configurez votre instance de webmail depuis le navigateur.",
+42
View File
@@ -3449,6 +3449,48 @@
"description": "התנתקות מהחשבון הזה במכשיר הזה."
}
},
"uxp2_settings": {
"search": {
"tray": "מגש המערכת אזור ההודעות רקע מזעור למגש tray",
"autostart": "הפעלה אוטומטית הפעלה עם Windows בעת הכניסה",
"default_app": "אפליקציית דואר ברירת מחדל תוכנת דואר mailto קישורי דואר",
"menu_bar": "שורת תפריטים תפריטים מקש Alt קובץ עריכה תצוגה",
"updates": "עדכונים עדכון גרסה חדשה בטא ערוץ",
"devtools": "כלי מפתחים מסוף בדיקה F12 ניפוי באגים devtools",
"taskbar": "שורת המשימות הבהוב Dock תשומת לב",
"quiet_hours": "שעות שקט נא לא להפריע השתקה לילה",
"telemetry": "נתוני שימוש טלמטריה סטטיסטיקה ניתוח",
"crash": "דוחות קריסה דוחות שגיאה קריסה",
"threads": "שרשורים שיחות קיבוץ threads",
"block_images": "חסימת תמונות תמונות חיצוניות טעינת תמונות",
"dark_mode": "מצב כהה מצב בהיר מצב לילה dark mode",
"timezone": "אזור זמן timezone",
"reading_pane": "חלונית קריאה חלונית תצוגה מקדימה תצוגה מפוצלת",
"week_start": "תחילת השבוע היום הראשון ראשון שני",
"empty_trash": "ריקון האשפה אשפה פריטים שנמחקו",
"do_not_disturb": "שעות שקט השתקה שקט"
},
"desktop_notifications": {
"title": "במחשב הזה",
"closed_label": "כשהחלון סגור",
"closed_description": "{appName} מודיע על דואר חדש כל עוד הוא פועל. כדי שימשיך לפעול במגש המערכת אחרי סגירת החלון, יש להפעיל זאת ב{page}."
},
"push_unsupported": "הדפדפן הזה לא יכול להציג התראות כשהאתר סגור.",
"backup_description_app": "אותן הגדרות במכשירים האחרים שלך.",
"sender_favicons": {
"label": "תמונות מאתרי חברות",
"description": "הצג סמלי אתרים כתמונות פרופיל עבור שולחים עסקיים"
},
"privacy_push": {
"label": "ממסר להתראות",
"description": "התראות כש{appName} סגור עוברות דרך ממסר push שלעולם אינו רואה את הדואר שלך. מפעילים ומכבים אותן בדף {page}."
},
"reset": {
"message": "כל ההגדרות יחזרו למצבן ההתחלתי. הדואר, המסננים, המענה האוטומטי והחתימות שלך יישארו כפי שהם.",
"sync_on": "המכשירים האחרים שלך משתמשים באותן הגדרות, ולכן גם הם ישתנו.",
"sync_off": "המכשירים האחרים שלך ישמרו על ההגדרות שלהם."
}
},
"setup": {
"title": "הגדרת Bulwark Webmail",
"subtitle": "הגדירו את מופע דואר הרשת שלכם מהדפדפן.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Kijelentkezés ebből a fiókból ezen az eszközön."
}
},
"uxp2_settings": {
"search": {
"tray": "tálca értesítési terület háttér tálcára kicsinyítés tray",
"autostart": "automatikus indítás indításkor Windows-zal együtt bejelentkezéskor",
"default_app": "alapértelmezett levelezőalkalmazás alapértelmezett levelezőprogram mailto e-mail-hivatkozások",
"menu_bar": "menüsor menük Alt billentyű Fájl Szerkesztés Nézet",
"updates": "frissítések frissítés új verzió béta csatorna",
"devtools": "fejlesztői eszközök devtools konzol vizsgálat F12 hibakeresés",
"taskbar": "tálca villogás Dock figyelem",
"quiet_hours": "csendes órák ne zavarjanak némítás éjszaka",
"telemetry": "használati adatok telemetria statisztika elemzés",
"crash": "összeomlási jelentések hibajelentések összeomlás",
"threads": "szálak beszélgetések csoportosítás threads",
"block_images": "képek letiltása külső képek képek betöltése",
"dark_mode": "sötét mód világos mód éjszakai mód dark mode",
"timezone": "időzóna timezone",
"reading_pane": "olvasóablak előnézeti ablak osztott nézet",
"week_start": "hét kezdete első nap hétfő vasárnap",
"empty_trash": "kuka ürítése kuka törölt elemek",
"do_not_disturb": "csendes órák némítás csend"
},
"desktop_notifications": {
"title": "Ezen a számítógépen",
"closed_label": "Bezárt ablakkal",
"closed_description": "A(z) {appName} szól az új levelekről, amíg fut. Ha azt szeretné, hogy az ablak bezárása után a tálcán fusson tovább, kapcsolja be itt: {page}."
},
"push_unsupported": "Ez a böngésző nem tud értesítést mutatni, ha a webhely be van zárva.",
"backup_description_app": "Ugyanazok a beállítások a többi eszközén.",
"sender_favicons": {
"label": "Képek a cégek webhelyeiről",
"description": "Weboldal ikonok megjelenítése profilképekként az üzleti feladók számára"
},
"privacy_push": {
"label": "Értesítési továbbító",
"description": "A bezárt {appName} értesítései egy push továbbítón mennek át, amely soha nem látja a leveleit. Ki- és bekapcsolni a(z) {page} oldalon lehet."
},
"reset": {
"message": "Minden beállítást visszaállít a kezdeti állapotra. A levelei, szűrői, automatikus válasza és aláírásai maradnak.",
"sync_on": "A többi eszköze ugyanazokat a beállításokat használja, ezért azok is megváltoznak.",
"sync_off": "A többi eszköze megtartja a beállításait."
}
},
"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.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Esci da questo account su questo dispositivo."
}
},
"uxp2_settings": {
"search": {
"tray": "area di notifica barra di sistema background riduci a icona tray",
"autostart": "avvio automatico avvio avvia con Windows all’accesso",
"default_app": "app di posta predefinita programma di posta predefinito mailto link email",
"menu_bar": "barra dei menu menu tasto Alt File Modifica Visualizza",
"updates": "aggiornamenti aggiornamento nuova versione beta canale",
"devtools": "strumenti per sviluppatori devtools console ispeziona F12 debug",
"taskbar": "barra delle applicazioni lampeggia Dock attenzione",
"quiet_hours": "ore di silenzio non disturbare silenzioso muto notte",
"telemetry": "dati di utilizzo telemetria statistiche analisi",
"crash": "segnalazioni di arresto anomalo segnalazioni errori crash",
"threads": "thread conversazioni raggruppa",
"block_images": "blocca immagini immagini esterne carica immagini",
"dark_mode": "modalità scura modalità chiara modalità notte tema scuro",
"timezone": "fuso orario timezone",
"reading_pane": "riquadro di lettura riquadro anteprima vista divisa",
"week_start": "inizio settimana primo giorno lunedì domenica",
"empty_trash": "svuota il cestino cestino elementi eliminati",
"do_not_disturb": "ore di silenzio muto silenzio"
},
"desktop_notifications": {
"title": "Su questo computer",
"closed_label": "Con la finestra chiusa",
"closed_description": "{appName} ti avvisa della nuova posta finché è in esecuzione. Per farlo restare nell’area di notifica dopo aver chiuso la finestra, attivalo in {page}."
},
"push_unsupported": "Questo browser non può mostrare notifiche quando il sito è chiuso.",
"backup_description_app": "Usa le stesse impostazioni sugli altri dispositivi.",
"sender_favicons": {
"label": "Immagini dai siti delle aziende",
"description": "Mostra le icone dei siti web come immagini profilo per i mittenti aziendali"
},
"privacy_push": {
"label": "Relay per le notifiche",
"description": "Le notifiche con {appName} chiuso passano da un relay push, che non vede mai la tua posta. Le attivi o disattivi nella pagina {page}."
},
"reset": {
"message": "Riporta tutte le impostazioni a come erano all’inizio. Posta, filtri, risposta automatica e firme restano come sono.",
"sync_on": "Gli altri dispositivi usano le stesse impostazioni, quindi cambiano anche loro.",
"sync_off": "Gli altri dispositivi mantengono le loro impostazioni."
}
},
"setup": {
"title": "Configurazione di Bulwark Webmail",
"subtitle": "Configura la tua istanza webmail dal browser.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "このデバイスでこのアカウントからサインアウトします。"
}
},
"uxp2_settings": {
"search": {
"tray": "トレイ システムトレイ 通知領域 バックグラウンド 最小化 tray",
"autostart": "自動起動 スタートアップ Windows と一緒に起動 サインイン時に起動",
"default_app": "既定のメールアプリ 既定のメールプログラム mailto メールリンク",
"menu_bar": "メニューバー メニュー Alt キー ファイル 編集 表示",
"updates": "アップデート 更新 新しいバージョン ベータ チャンネル",
"devtools": "開発者ツール devtools コンソール 検証 F12 デバッグ",
"taskbar": "タスクバー 点滅 Dock 注意",
"quiet_hours": "おやすみ時間 通知停止 サイレント ミュート 夜間",
"telemetry": "使用状況 テレメトリ 統計 分析",
"crash": "クラッシュレポート エラーレポート クラッシュ",
"threads": "スレッド 会話 グループ化 threads",
"block_images": "画像をブロック 外部画像 画像の読み込み",
"dark_mode": "ダークモード ライトモード 夜間モード dark mode",
"timezone": "タイムゾーン 時間帯 timezone",
"reading_pane": "閲覧ウィンドウ プレビューウィンドウ 分割表示",
"week_start": "週の始まり 週の最初の日 月曜日 日曜日",
"empty_trash": "ごみ箱を空にする ごみ箱 削除済み",
"do_not_disturb": "おやすみ時間 ミュート 消音"
},
"desktop_notifications": {
"title": "このコンピューター",
"closed_label": "ウィンドウを閉じたとき",
"closed_description": "{appName} は動いている間、新着メールをお知らせします。ウィンドウを閉じた後もトレイで動かし続けるには、{page} でオンにしてください。"
},
"push_unsupported": "このブラウザでは、サイトを閉じていると通知を表示できません。",
"backup_description_app": "ほかのデバイスでも同じ設定を使います。",
"sender_favicons": {
"label": "会社のウェブサイトの画像",
"description": "ビジネス送信者のプロフィール画像としてウェブサイトアイコンを表示"
},
"privacy_push": {
"label": "通知のリレー",
"description": "{appName} を閉じているときの通知はプッシュリレーを通りますが、リレーがメールを見ることはありません。オン・オフは {page} ページで切り替えます。"
},
"reset": {
"message": "すべての設定を最初の状態に戻します。メール、フィルター、自動返信、署名はそのまま残ります。",
"sync_on": "ほかのデバイスも同じ設定を使っているため、そちらも変わります。",
"sync_off": "ほかのデバイスの設定はそのままです。"
}
},
"setup": {
"title": "Bulwark Webmail のセットアップ",
"subtitle": "ブラウザーから Web メールのインスタンスを設定します。",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "이 기기에서 이 계정을 로그아웃해요."
}
},
"uxp2_settings": {
"search": {
"tray": "트레이 시스템 트레이 알림 영역 백그라운드 최소화 tray",
"autostart": "자동 시작 시작 프로그램 Windows와 함께 시작 로그인 시 시작",
"default_app": "기본 메일 앱 기본 이메일 프로그램 mailto 메일 링크",
"menu_bar": "메뉴 표시줄 메뉴 Alt 키 파일 편집 보기",
"updates": "업데이트 새 버전 베타 채널",
"devtools": "개발자 도구 devtools 콘솔 검사 F12 디버깅",
"taskbar": "작업 표시줄 깜박임 Dock 주의",
"quiet_hours": "방해 금지 시간 방해 금지 무음 음소거 야간",
"telemetry": "사용 정보 원격 분석 통계 분석",
"crash": "충돌 보고서 오류 보고서 충돌",
"threads": "스레드 대화 묶기 threads",
"block_images": "이미지 차단 외부 이미지 이미지 불러오기",
"dark_mode": "다크 모드 라이트 모드 야간 모드 dark mode",
"timezone": "시간대 표준 시간대 timezone",
"reading_pane": "읽기 창 미리 보기 창 분할 보기",
"week_start": "주 시작 첫째 요일 월요일 일요일",
"empty_trash": "휴지통 비우기 휴지통 삭제된 항목",
"do_not_disturb": "방해 금지 시간 음소거 무음"
},
"desktop_notifications": {
"title": "이 컴퓨터에서",
"closed_label": "창을 닫았을 때",
"closed_description": "{appName}은(는) 실행 중인 동안 새 메일을 알려 드려요. 창을 닫은 뒤에도 트레이에서 계속 실행하려면 {page}에서 켜세요."
},
"push_unsupported": "이 브라우저는 웹사이트가 닫혀 있을 때 알림을 표시할 수 없어요.",
"backup_description_app": "다른 기기에서도 같은 설정을 사용해요.",
"sender_favicons": {
"label": "회사 웹사이트의 사진",
"description": "비즈니스 메일의 경우 해당 웹사이트의 아이콘을 프로필 사진으로 보여줘요"
},
"privacy_push": {
"label": "알림 릴레이",
"description": "{appName}이(가) 닫혀 있을 때의 알림은 푸시 릴레이를 거치며, 릴레이는 메일을 절대 보지 않아요. {page} 페이지에서 켜고 끌 수 있어요."
},
"reset": {
"message": "모든 설정을 처음 상태로 되돌려요. 메일, 필터, 자동 응답, 서명은 그대로 남아요.",
"sync_on": "다른 기기도 같은 설정을 사용하므로 함께 바뀌어요.",
"sync_off": "다른 기기의 설정은 그대로예요."
}
},
"setup": {
"title": "Bulwark Webmail 설정",
"subtitle": "브라우저에서 웹메일 인스턴스를 설정하세요.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Izrakstīties no šī konta šajā ierīcē."
}
},
"uxp2_settings": {
"search": {
"tray": "tekne sistēmas tekne paziņojumu apgabals fonā minimizēt tray",
"autostart": "automātiska palaišana startēšana palaist ar Windows pierakstoties",
"default_app": "noklusējuma pasta lietotne noklusējuma e-pasta programma mailto e-pasta saites",
"menu_bar": "izvēlņu josla izvēlnes Alt taustiņš Fails Rediģēt Skats",
"updates": "atjauninājumi atjauninājums jauna versija beta kanāls",
"devtools": "izstrādātāja rīki devtools konsole pārbaudīt F12 atkļūdošana",
"taskbar": "uzdevumjosla mirgošana Dock uzmanība",
"quiet_hours": "klusuma stundas netraucēt klusums nakts",
"telemetry": "lietojuma informācija telemetrija statistika analītika",
"crash": "avāriju ziņojumi kļūdu ziņojumi avārija",
"threads": "pavedieni sarunas grupēt threads",
"block_images": "bloķēt attēlus ārējie attēli ielādēt attēlus",
"dark_mode": "tumšais režīms gaišais režīms nakts režīms dark mode",
"timezone": "laika josla timezone",
"reading_pane": "lasīšanas rūts priekšskatījuma rūts dalīts skats",
"week_start": "nedēļas sākums pirmā diena pirmdiena svētdiena",
"empty_trash": "iztukšot atkritni atkritne dzēstie vienumi",
"do_not_disturb": "klusuma stundas izslēgt skaņu klusums"
},
"desktop_notifications": {
"title": "Šajā datorā",
"closed_label": "Ar aizvērtu logu",
"closed_description": "{appName} ziņo par jaunu pastu, kamēr tā darbojas. Lai pēc loga aizvēršanas tā turpinātu darboties teknē, ieslēdziet to sadaļā {page}."
},
"push_unsupported": "Šī pārlūkprogramma nevar rādīt paziņojumus, kad vietne ir aizvērta.",
"backup_description_app": "Izmantojiet tos pašus iestatījumus citās savās ierīcēs.",
"sender_favicons": {
"label": "Attēli no uzņēmumu vietnēm",
"description": "Rādīt vietņu ikonas kā sūtītāju avatarus"
},
"privacy_push": {
"label": "Paziņojumu retranslators",
"description": "Paziņojumi, kamēr {appName} ir aizvērta, iet caur push retranslatoru, kas nekad neredz jūsu pastu. Tos ieslēdz vai izslēdz lapā {page}."
},
"reset": {
"message": "Atjauno visus iestatījumus sākotnējā stāvoklī. Jūsu pasts, filtri, automātiskā atbilde un paraksti paliek, kā ir.",
"sync_on": "Jūsu citas ierīces izmanto tos pašus iestatījumus, tāpēc mainās arī tās.",
"sync_off": "Jūsu citas ierīces saglabā savus iestatījumus."
}
},
"setup": {
"title": "Bulwark Webmail iestatīšana",
"subtitle": "Iestatiet savu tīmekļa pasta instanci pārlūkā.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Энэ төхөөрөмж дээр энэ бүртгэлээс гарах."
}
},
"uxp2_settings": {
"search": {
"tray": "тавиур системийн тавиур мэдэгдлийн хэсэг арын горим жижигрүүлэх tray",
"autostart": "автоматаар эхлэх эхлэл Windows-той хамт эхлэх нэвтрэхэд",
"default_app": "үндсэн шуудангийн апп үндсэн имэйл програм mailto имэйлийн холбоос",
"menu_bar": "цэсний мөр цэс Alt товч Файл Засах Харах",
"updates": "шинэчлэлт шинэ хувилбар бета суваг",
"devtools": "хөгжүүлэгчийн хэрэгсэл devtools консол шалгах F12 алдаа засах",
"taskbar": "ажлын самбар анивчих Dock анхаарал",
"quiet_hours": "чимээгүй цаг саад бүү болго дуугүй шөнө",
"telemetry": "ашиглалтын мэдээлэл телеметр статистик шинжилгээ",
"crash": "гацалтын тайлан алдааны тайлан гацалт",
"threads": "урсгал харилцан яриа бүлэглэх threads",
"block_images": "зураг хаах гадны зураг зураг ачаалах",
"dark_mode": "харанхуй горим гэрэлтэй горим шөнийн горим dark mode",
"timezone": "цагийн бүс timezone",
"reading_pane": "унших хэсэг урьдчилан харах хэсэг хуваасан харагдац",
"week_start": "долоо хоногийн эхлэл эхний өдөр даваа ням",
"empty_trash": "хогийн савыг хоослох хогийн сав устгасан зүйлс",
"do_not_disturb": "чимээгүй цаг дуугүй чимээгүй"
},
"desktop_notifications": {
"title": "Энэ компьютер дээр",
"closed_label": "Цонх хаалттай үед",
"closed_description": "{appName} ажиллаж байх хугацаанд шинэ захидлын тухай мэдэгдэнэ. Цонхоо хаасны дараа тавиур дээр үргэлжлүүлэн ажиллуулахыг хүсвэл {page} хэсэгт асаана уу."
},
"push_unsupported": "Энэ хөтөч вэбсайт хаалттай үед мэдэгдэл харуулж чадахгүй.",
"backup_description_app": "Бусад төхөөрөмж дээрээ ижил тохиргоог ашиглана.",
"sender_favicons": {
"label": "Компаниудын вэбсайтын зураг",
"description": "Вэб сайтын дүрсийг бизнес илгээгчдэд профайл зураг болгон харуулах"
},
"privacy_push": {
"label": "Мэдэгдлийн реле",
"description": "{appName} хаалттай үеийн мэдэгдэл push релеээр дамждаг бөгөөд реле таны захидлыг хэзээ ч хардаггүй. Тэдгээрийг {page} хуудсанд асааж унтраана."
},
"reset": {
"message": "Бүх тохиргоог анхны байдалд нь буцаана. Таны захидал, шүүлтүүр, автомат хариу, гарын үсэг хэвээр үлдэнэ.",
"sync_on": "Таны бусад төхөөрөмж ижил тохиргоог ашигладаг тул тэд ч мөн өөрчлөгдөнө.",
"sync_off": "Таны бусад төхөөрөмж тохиргоогоо хадгална."
}
},
"setup": {
"title": "Bulwark Webmail-ийн тохиргоо",
"subtitle": "Вэб шуудангийн инстансаа хөтчөөс тохируулна уу.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Logg ut av denne kontoen på denne enheten."
}
},
"uxp2_settings": {
"search": {
"tray": "systemstatusfelt varslingsområde bakgrunn minimer tray",
"autostart": "autostart oppstart start med Windows ved pålogging",
"default_app": "standard e-postapp standard e-postprogram mailto e-postlenker",
"menu_bar": "menylinje menyer Alt-tast Fil Rediger Vis",
"updates": "oppdateringer oppdatering ny versjon beta kanal",
"devtools": "utviklerverktøy devtools konsoll inspiser F12 feilsøking",
"taskbar": "oppgavelinje blink Dock oppmerksomhet",
"quiet_hours": "stille timer ikke forstyrr lydløs natt",
"telemetry": "bruksdata telemetri statistikk analyse",
"crash": "krasjrapporter feilrapporter krasj",
"threads": "tråder samtaler samtalevisning threads",
"block_images": "blokker bilder eksterne bilder last inn bilder",
"dark_mode": "mørk modus lys modus nattmodus dark mode",
"timezone": "tidssone timezone",
"reading_pane": "leserute forhåndsvisningsrute delt visning",
"week_start": "ukestart første ukedag mandag søndag",
"empty_trash": "tøm papirkurven papirkurv slettede elementer",
"do_not_disturb": "stille timer lydløs stillhet"
},
"desktop_notifications": {
"title": "På denne datamaskinen",
"closed_label": "Når vinduet er lukket",
"closed_description": "{appName} varsler om ny e-post så lenge den kjører. Slå det på under {page} hvis den skal kjøre videre i systemstatusfeltet når du lukker vinduet."
},
"push_unsupported": "Denne nettleseren kan ikke vise varsler når nettstedet er lukket.",
"backup_description_app": "Bruk de samme innstillingene på de andre enhetene dine.",
"sender_favicons": {
"label": "Bilder fra bedriftenes nettsteder",
"description": "Vis nettstedsikoner som profilbilder for bedriftsavsendere"
},
"privacy_push": {
"label": "Relé for varsler",
"description": "Varsler mens {appName} er lukket går gjennom et push-relé, som aldri ser e-posten din. Du slår dem av og på på siden {page}."
},
"reset": {
"message": "Setter alle innstillinger tilbake til slik de var fra start. E-post, filtre, autosvar og signaturer blir som de er.",
"sync_on": "De andre enhetene dine bruker de samme innstillingene, så de endres også.",
"sync_off": "De andre enhetene dine beholder innstillingene sine."
}
},
"setup": {
"title": "Oppsett av Bulwark Webmail",
"subtitle": "Sett opp webmail-instansen din i nettleseren.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Dit account op dit apparaat afmelden."
}
},
"uxp2_settings": {
"search": {
"tray": "systeemvak meldingsgebied achtergrond minimaliseren tray",
"autostart": "automatisch starten opstarten starten met Windows bij aanmelden",
"default_app": "standaard mail-app standaard e-mailprogramma mailto e-maillinks",
"menu_bar": "menubalk menu’s Alt-toets Bestand Bewerken Beeld",
"updates": "updates bijwerken nieuwe versie bèta kanaal",
"devtools": "ontwikkelaarstools devtools console inspecteren F12 debuggen",
"taskbar": "taakbalk knipperen Dock aandacht",
"quiet_hours": "stille uren niet storen stil dempen nacht",
"telemetry": "gebruiksgegevens telemetrie statistieken analyse",
"crash": "crashrapporten foutrapporten crash",
"threads": "threads gesprekken groeperen conversatie",
"block_images": "afbeeldingen blokkeren externe afbeeldingen afbeeldingen laden",
"dark_mode": "donkere modus lichte modus nachtmodus dark mode",
"timezone": "tijdzone timezone",
"reading_pane": "leesvenster voorbeeldvenster gesplitste weergave",
"week_start": "begin van de week eerste dag maandag zondag",
"empty_trash": "prullenbak legen prullenbak verwijderde items",
"do_not_disturb": "stille uren dempen stilte"
},
"desktop_notifications": {
"title": "Op deze computer",
"closed_label": "Met het venster dicht",
"closed_description": "{appName} meldt nieuwe mail zolang het draait. Zet dit aan bij {page} als het na het sluiten van het venster in het systeemvak moet blijven draaien."
},
"push_unsupported": "Deze browser kan geen meldingen tonen als de website dicht is.",
"backup_description_app": "Gebruik dezelfde instellingen op uw andere apparaten.",
"sender_favicons": {
"label": "Afbeeldingen van bedrijfswebsites",
"description": "Toon websitepictogrammen als profielfoto's voor zakelijke afzenders"
},
"privacy_push": {
"label": "Relay voor meldingen",
"description": "Meldingen terwijl {appName} dicht is gaan via een push-relay, die uw mail nooit ziet. U zet ze aan of uit op de pagina {page}."
},
"reset": {
"message": "Zet alle instellingen terug zoals ze in het begin waren. Uw mail, filters, afwezigheidsbericht en handtekeningen blijven zoals ze zijn.",
"sync_on": "Uw andere apparaten gebruiken dezelfde instellingen, dus die veranderen ook.",
"sync_off": "Uw andere apparaten houden hun instellingen."
}
},
"setup": {
"title": "Bulwark Webmail instellen",
"subtitle": "Stel je webmailinstantie in vanuit de browser.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Wyloguj to konto na tym urządzeniu."
}
},
"uxp2_settings": {
"search": {
"tray": "zasobnik zasobnik systemowy obszar powiadomień w tle minimalizuj tray",
"autostart": "autostart uruchamianie uruchom z Windows przy logowaniu",
"default_app": "domyślna aplikacja pocztowa domyślny program pocztowy mailto linki e-mail",
"menu_bar": "pasek menu menu klawisz Alt Plik Edycja Widok",
"updates": "aktualizacje aktualizacja nowa wersja beta kanał",
"devtools": "narzędzia deweloperskie devtools konsola zbadaj F12 debugowanie",
"taskbar": "pasek zadań miganie Dock uwaga",
"quiet_hours": "godziny ciszy nie przeszkadzać wycisz noc",
"telemetry": "dane o użyciu telemetria statystyki analityka",
"crash": "raporty awarii raporty błędów awaria",
"threads": "wątki rozmowy grupowanie threads",
"block_images": "blokuj obrazy obrazy zewnętrzne wczytuj obrazy",
"dark_mode": "tryb ciemny tryb jasny tryb nocny dark mode",
"timezone": "strefa czasowa timezone",
"reading_pane": "okienko odczytu okienko podglądu widok dzielony",
"week_start": "początek tygodnia pierwszy dzień poniedziałek niedziela",
"empty_trash": "opróżnij kosz kosz usunięte elementy",
"do_not_disturb": "godziny ciszy wycisz cisza"
},
"desktop_notifications": {
"title": "Na tym komputerze",
"closed_label": "Przy zamkniętym oknie",
"closed_description": "{appName} powiadamia o nowej poczcie, dopóki działa. Aby po zamknięciu okna działał dalej w zasobniku, włącz to w sekcji {page}."
},
"push_unsupported": "Ta przeglądarka nie może pokazywać powiadomień, gdy strona jest zamknięta.",
"backup_description_app": "Używaj tych samych ustawień na innych urządzeniach.",
"sender_favicons": {
"label": "Obrazy ze stron firm",
"description": "Pokazuj ikony stron internetowych jako zdjęcia profilowe nadawców firmowych"
},
"privacy_push": {
"label": "Przekaźnik powiadomień",
"description": "Powiadomienia przy zamkniętym {appName} idą przez przekaźnik push, który nigdy nie widzi Twojej poczty. Włączasz je i wyłączasz na stronie {page}."
},
"reset": {
"message": "Przywraca wszystkie ustawienia do stanu początkowego. Twoja poczta, filtry, autoodpowiedź i podpisy zostają bez zmian.",
"sync_on": "Twoje inne urządzenia używają tych samych ustawień, więc też się zmienią.",
"sync_off": "Twoje inne urządzenia zachowają swoje ustawienia."
}
},
"setup": {
"title": "Konfiguracja Bulwark Webmail",
"subtitle": "Skonfiguruj swoją instancję poczty w przeglądarce.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Sair desta conta neste dispositivo."
}
},
"uxp2_settings": {
"search": {
"tray": "bandeja bandeja do sistema área de notificação segundo plano minimizar tray",
"autostart": "início automático arranque iniciar com o Windows ao iniciar sessão",
"default_app": "aplicação de correio predefinida programa de e-mail predefinido mailto ligações de e-mail",
"menu_bar": "barra de menus menus tecla Alt Ficheiro Editar Ver",
"updates": "atualizações atualizar nova versão beta canal",
"devtools": "ferramentas de programador devtools consola inspecionar F12 depuração",
"taskbar": "barra de tarefas piscar Dock atenção",
"quiet_hours": "horas de silêncio não incomodar silêncio silenciar noite",
"telemetry": "informações de utilização telemetria estatísticas análise",
"crash": "relatórios de falhas relatórios de erros falha",
"threads": "tópicos conversas agrupar threads",
"block_images": "bloquear imagens imagens externas carregar imagens",
"dark_mode": "modo escuro modo claro modo noturno tema escuro",
"timezone": "fuso horário timezone",
"reading_pane": "painel de leitura painel de pré-visualização vista dividida",
"week_start": "início da semana primeiro dia segunda-feira domingo",
"empty_trash": "esvaziar o lixo lixo itens eliminados",
"do_not_disturb": "horas de silêncio silenciar silêncio"
},
"desktop_notifications": {
"title": "Neste computador",
"closed_label": "Com a janela fechada",
"closed_description": "O {appName} avisa-o de correio novo enquanto estiver a funcionar. Para continuar na bandeja depois de fechar a janela, ative isso em {page}."
},
"push_unsupported": "Este navegador não consegue mostrar notificações quando o site está fechado.",
"backup_description_app": "Use as mesmas definições nos seus outros dispositivos.",
"sender_favicons": {
"label": "Imagens dos sites das empresas",
"description": "Exibir ícones de sites como fotos de perfil para remetentes empresariais"
},
"privacy_push": {
"label": "Retransmissor de notificações",
"description": "As notificações com o {appName} fechado passam por um retransmissor push, que nunca vê o seu correio. Ative-as ou desative-as na página {page}."
},
"reset": {
"message": "Repõe todas as definições como estavam no início. O seu correio, filtros, resposta automática e assinaturas ficam como estão.",
"sync_on": "Os seus outros dispositivos usam as mesmas definições, por isso também mudam.",
"sync_off": "Os seus outros dispositivos mantêm as definições deles."
}
},
"setup": {
"title": "Configuração do Bulwark Webmail",
"subtitle": "Configure a sua instância de webmail a partir do navegador.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Deconectați acest cont pe acest dispozitiv."
}
},
"uxp2_settings": {
"search": {
"tray": "zona de notificări bara de sistem fundal minimizare tray",
"autostart": "pornire automată la pornire pornește cu Windows la conectare",
"default_app": "aplicație de e-mail implicită program de e-mail implicit mailto linkuri de e-mail",
"menu_bar": "bara de meniu meniuri tasta Alt Fișier Editare Vizualizare",
"updates": "actualizări actualizare versiune nouă beta canal",
"devtools": "instrumente pentru dezvoltatori devtools consolă inspectare F12 depanare",
"taskbar": "bara de activități clipire Dock atenție",
"quiet_hours": "ore de liniște nu deranjați silențios fără sunet noapte",
"telemetry": "informații de utilizare telemetrie statistici analiză",
"crash": "rapoarte de blocare rapoarte de erori blocare",
"threads": "fire conversații grupare threads",
"block_images": "blocare imagini imagini externe încărcare imagini",
"dark_mode": "mod întunecat mod luminos mod noapte dark mode",
"timezone": "fus orar timezone",
"reading_pane": "panou de citire panou de previzualizare vizualizare împărțită",
"week_start": "începutul săptămânii prima zi luni duminică",
"empty_trash": "golire coș de gunoi coș de gunoi elemente șterse",
"do_not_disturb": "ore de liniște fără sunet liniște"
},
"desktop_notifications": {
"title": "Pe acest computer",
"closed_label": "Cu fereastra închisă",
"closed_description": "{appName} vă anunță despre e-mailurile noi cât timp rulează. Ca să ruleze în continuare în zona de notificări după ce închideți fereastra, activați asta în {page}."
},
"push_unsupported": "Acest browser nu poate afișa notificări când site-ul este închis.",
"backup_description_app": "Folosiți aceleași setări pe celelalte dispozitive.",
"sender_favicons": {
"label": "Imagini de pe site-urile companiilor",
"description": "Afișați pictogramele site-urilor web ca imagini de profil pentru expeditorii de afaceri"
},
"privacy_push": {
"label": "Releu pentru notificări",
"description": "Notificările cu {appName} închis trec printr-un releu push, care nu vă vede niciodată e-mailurile. Le activați sau dezactivați pe pagina {page}."
},
"reset": {
"message": "Readuce toate setările la cum erau la început. E-mailurile, filtrele, răspunsul automat și semnăturile rămân cum sunt.",
"sync_on": "Celelalte dispozitive folosesc aceleași setări, așa că se schimbă și ele.",
"sync_off": "Celelalte dispozitive își păstrează setările."
}
},
"setup": {
"title": "Configurarea Bulwark Webmail",
"subtitle": "Configurați instanța de webmail din browser.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Выйти из этой учётной записи на этом устройстве."
}
},
"uxp2_settings": {
"search": {
"tray": "трей системный трей область уведомлений фон свернуть в трей tray",
"autostart": "автозапуск запуск при старте запуск вместе с Windows при входе",
"default_app": "почтовое приложение по умолчанию почтовая программа mailto ссылки на почту",
"menu_bar": "строка меню меню клавиша Alt Файл Правка Вид",
"updates": "обновления обновление новая версия бета канал",
"devtools": "инструменты разработчика devtools консоль исследовать F12 отладка",
"taskbar": "панель задач мигание Dock внимание",
"quiet_hours": "тихие часы не беспокоить без звука ночь",
"telemetry": "данные об использовании телеметрия статистика аналитика",
"crash": "отчёты о сбоях отчёты об ошибках сбой",
"threads": "цепочки беседы группировать threads",
"block_images": "блокировать изображения внешние изображения загружать картинки",
"dark_mode": "тёмный режим светлый режим ночной режим тёмная тема",
"timezone": "часовой пояс timezone",
"reading_pane": "область чтения область просмотра разделённый вид",
"week_start": "начало недели первый день понедельник воскресенье",
"empty_trash": "очистить корзину корзина удалённые",
"do_not_disturb": "тихие часы без звука тишина"
},
"desktop_notifications": {
"title": "На этом компьютере",
"closed_label": "При закрытом окне",
"closed_description": "{appName} сообщает о новой почте, пока работает. Чтобы после закрытия окна он продолжал работать в трее, включите это в разделе «{page}»."
},
"push_unsupported": "Этот браузер не может показывать уведомления, когда сайт закрыт.",
"backup_description_app": "Одни и те же настройки на всех ваших устройствах.",
"sender_favicons": {
"label": "Изображения с сайтов компаний",
"description": "Показывать иконки сайтов как аватары для корпоративных отправителей"
},
"privacy_push": {
"label": "Ретранслятор уведомлений",
"description": "Уведомления при закрытом {appName} идут через push-ретранслятор, который никогда не видит вашу почту. Они включаются и выключаются на странице «{page}»."
},
"reset": {
"message": "Вернуть все настройки к исходному состоянию. Ваша почта, фильтры, автоответ и подписи останутся как есть.",
"sync_on": "Ваши другие устройства используют те же настройки, поэтому они тоже изменятся.",
"sync_off": "Ваши другие устройства сохранят свои настройки."
}
},
"setup": {
"title": "Настройка Bulwark Webmail",
"subtitle": "Настройте свой экземпляр веб-почты в браузере.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Odhlásiť tento účet na tomto zariadení."
}
},
"uxp2_settings": {
"search": {
"tray": "oblasť oznámení systémová lišta pozadie minimalizovať tray",
"autostart": "automatické spustenie po štarte spustiť s Windows pri prihlásení",
"default_app": "predvolená poštová aplikácia predvolený e-mailový program mailto odkazy na e-mail",
"menu_bar": "panel ponúk ponuky kláves Alt Súbor Upraviť Zobraziť",
"updates": "aktualizácie aktualizácia nová verzia beta kanál",
"devtools": "nástroje pre vývojárov devtools konzola preskúmať F12 ladenie",
"taskbar": "panel úloh blikanie Dock pozornosť",
"quiet_hours": "tiché hodiny nerušiť stlmiť noc",
"telemetry": "údaje o používaní telemetria štatistiky analytika",
"crash": "hlásenia o páde chybové hlásenia pád",
"threads": "vlákna konverzácie zoskupiť threads",
"block_images": "blokovať obrázky externé obrázky načítať obrázky",
"dark_mode": "tmavý režim svetlý režim nočný režim dark mode",
"timezone": "časové pásmo timezone",
"reading_pane": "tabla čítania tabla ukážky rozdelené zobrazenie",
"week_start": "začiatok týždňa prvý deň pondelok nedeľa",
"empty_trash": "vyprázdniť kôš kôš odstránené položky",
"do_not_disturb": "tiché hodiny stlmiť ticho"
},
"desktop_notifications": {
"title": "Na tomto počítači",
"closed_label": "Pri zatvorenom okne",
"closed_description": "{appName} vás upozorní na novú poštu, kým beží. Aby po zatvorení okna bežal ďalej v oblasti oznámení, zapnite to v časti {page}."
},
"push_unsupported": "Tento prehliadač nemôže zobrazovať upozornenia, keď je web zatvorený.",
"backup_description_app": "Používajte rovnaké nastavenia na ďalších zariadeniach.",
"sender_favicons": {
"label": "Obrázky z webov firiem",
"description": "Zobrazovať ikony webových stránok ako profilové obrázky firemných odosielateľov"
},
"privacy_push": {
"label": "Relay pre upozornenia",
"description": "Upozornenia pri zatvorenom {appName} idú cez push relay, ktorý nikdy nevidí vašu poštu. Zapínate a vypínate ich na stránke {page}."
},
"reset": {
"message": "Vráti všetky nastavenia do pôvodného stavu. Vaša pošta, filtre, automatická odpoveď a podpisy zostanú, ako sú.",
"sync_on": "Vaše ďalšie zariadenia používajú rovnaké nastavenia, takže sa zmenia tiež.",
"sync_off": "Vaše ďalšie zariadenia si nastavenia ponechajú."
}
},
"setup": {
"title": "Nastavenie Bulwark Webmail",
"subtitle": "Nastavte svoju inštanciu webmailu v prehliadači.",
+42
View File
@@ -6505,6 +6505,48 @@
"description": "ออกจากระบบบัญชีนี้บนอุปกรณ์นี้"
}
},
"uxp2_settings": {
"search": {
"tray": "ถาดระบบ พื้นที่แจ้งเตือน พื้นหลัง ย่อลงถาด tray",
"autostart": "เริ่มอัตโนมัติ เริ่มพร้อม Windows เมื่อลงชื่อเข้าใช้",
"default_app": "แอปอีเมลเริ่มต้น โปรแกรมอีเมลเริ่มต้น mailto ลิงก์อีเมล",
"menu_bar": "แถบเมนู เมนู ปุ่ม Alt ไฟล์ แก้ไข มุมมอง",
"updates": "อัปเดต เวอร์ชันใหม่ เบต้า ช่อง",
"devtools": "เครื่องมือนักพัฒนา devtools คอนโซล ตรวจสอบ F12 ดีบัก",
"taskbar": "แถบงาน กะพริบ Dock ความสนใจ",
"quiet_hours": "ช่วงเวลาเงียบ ห้ามรบกวน ปิดเสียง กลางคืน",
"telemetry": "ข้อมูลการใช้งาน การวัดระยะไกล สถิติ การวิเคราะห์",
"crash": "รายงานข้อขัดข้อง รายงานข้อผิดพลาด แครช",
"threads": "เธรด การสนทนา จัดกลุ่ม threads",
"block_images": "บล็อกรูปภาพ รูปภาพภายนอก โหลดรูปภาพ",
"dark_mode": "โหมดมืด โหมดสว่าง โหมดกลางคืน dark mode",
"timezone": "เขตเวลา timezone",
"reading_pane": "บานหน้าต่างการอ่าน บานหน้าต่างแสดงตัวอย่าง มุมมองแบ่ง",
"week_start": "วันเริ่มต้นสัปดาห์ วันแรก วันจันทร์ วันอาทิตย์",
"empty_trash": "ล้างถังขยะ ถังขยะ รายการที่ลบ",
"do_not_disturb": "ช่วงเวลาเงียบ ปิดเสียง เงียบ"
},
"desktop_notifications": {
"title": "บนคอมพิวเตอร์เครื่องนี้",
"closed_label": "เมื่อปิดหน้าต่าง",
"closed_description": "{appName} จะแจ้งอีเมลใหม่ตราบที่ยังทำงานอยู่ หากต้องการให้ทำงานต่อในถาดระบบหลังปิดหน้าต่าง ให้เปิดใน {page}"
},
"push_unsupported": "เบราว์เซอร์นี้แสดงการแจ้งเตือนไม่ได้เมื่อปิดเว็บไซต์",
"backup_description_app": "ใช้การตั้งค่าเดียวกันบนอุปกรณ์อื่นของคุณ",
"sender_favicons": {
"label": "รูปภาพจากเว็บไซต์ของบริษัท",
"description": "แสดงไอคอนเว็บไซต์เป็นรูปโปรไฟล์สำหรับผู้ส่งที่เป็นองค์กรธุรกิจ"
},
"privacy_push": {
"label": "รีเลย์การแจ้งเตือน",
"description": "การแจ้งเตือนขณะปิด {appName} จะผ่านรีเลย์พุช ซึ่งไม่เคยเห็นอีเมลของคุณ คุณเปิดหรือปิดได้ในหน้า {page}"
},
"reset": {
"message": "คืนการตั้งค่าทั้งหมดเป็นแบบตอนเริ่มต้น อีเมล ตัวกรอง การตอบกลับอัตโนมัติ และลายเซ็นของคุณยังคงเหมือนเดิม",
"sync_on": "อุปกรณ์อื่นของคุณใช้การตั้งค่าเดียวกัน จึงเปลี่ยนตามไปด้วย",
"sync_off": "อุปกรณ์อื่นของคุณยังคงใช้การตั้งค่าเดิม"
}
},
"setup": {
"title": "ตั้งค่า Bulwark Webmail",
"subtitle": "ตั้งค่าอินสแตนซ์เว็บเมลของคุณจากเบราว์เซอร์",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Bu cihazda bu hesabın oturumunu kapatın."
}
},
"uxp2_settings": {
"search": {
"tray": "sistem tepsisi bildirim alanı arka plan tepsiye küçült tray",
"autostart": "otomatik başlatma başlangıç Windows ile başlat oturum açınca",
"default_app": "varsayılan posta uygulaması varsayılan e-posta programı mailto e-posta bağlantıları",
"menu_bar": "menü çubuğu menüler Alt tuşu Dosya Düzen Görünüm",
"updates": "güncellemeler güncelleme yeni sürüm beta kanal",
"devtools": "geliştirici araçları devtools konsol incele F12 hata ayıklama",
"taskbar": "görev çubuğu yanıp sönme Dock dikkat",
"quiet_hours": "sessiz saatler rahatsız etmeyin sessiz gece",
"telemetry": "kullanım bilgisi telemetri istatistik analiz",
"crash": "çökme raporları hata raporları çökme",
"threads": "ileti dizileri konuşmalar gruplama threads",
"block_images": "resimleri engelle dış resimler resimleri yükle",
"dark_mode": "karanlık mod açık mod gece modu dark mode",
"timezone": "saat dilimi timezone",
"reading_pane": "okuma bölmesi önizleme bölmesi bölünmüş görünüm",
"week_start": "hafta başlangıcı ilk gün pazartesi pazar",
"empty_trash": "çöp kutusunu boşalt çöp kutusu silinen öğeler",
"do_not_disturb": "sessiz saatler sessize al sessizlik"
},
"desktop_notifications": {
"title": "Bu bilgisayarda",
"closed_label": "Pencere kapalıyken",
"closed_description": "{appName} çalıştığı sürece yeni postaları bildirir. Pencereyi kapattıktan sonra sistem tepsisinde çalışmaya devam etmesi için bunu {page} bölümünde açın."
},
"push_unsupported": "Bu tarayıcı, web sitesi kapalıyken bildirim gösteremez.",
"backup_description_app": "Diğer cihazlarınızda da aynı ayarları kullanın.",
"sender_favicons": {
"label": "Şirket web sitelerinden resimler",
"description": "İş göndericileri için profil resmi olarak web sitesi simgelerini göster"
},
"privacy_push": {
"label": "Bildirim aktarıcısı",
"description": "{appName} kapalıyken gelen bildirimler, e-postanızı asla görmeyen bir push aktarıcısından geçer. Bunları {page} sayfasında açıp kapatırsınız."
},
"reset": {
"message": "Tüm ayarları başlangıçtaki hâline döndürür. Postalarınız, filtreleriniz, otomatik yanıtınız ve imzalarınız olduğu gibi kalır.",
"sync_on": "Diğer cihazlarınız aynı ayarları kullanıyor, bu yüzden onlar da değişir.",
"sync_off": "Diğer cihazlarınız kendi ayarlarını korur."
}
},
"setup": {
"title": "Bulwark Webmail kurulumu",
"subtitle": "Webmail örneğinizi tarayıcıdan yapılandırın.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "Вийти з цього облікового запису на цьому пристрої."
}
},
"uxp2_settings": {
"search": {
"tray": "трей системний трей область сповіщень фон згорнути в трей tray",
"autostart": "автозапуск запуск під час старту запуск разом із Windows під час входу",
"default_app": "поштовий застосунок за замовчуванням поштова програма mailto посилання на пошту",
"menu_bar": "рядок меню меню клавіша Alt Файл Редагування Вигляд",
"updates": "оновлення нова версія бета канал",
"devtools": "інструменти розробника devtools консоль дослідити F12 налагодження",
"taskbar": "панель завдань блимання Dock увага",
"quiet_hours": "тихі години не турбувати без звуку ніч",
"telemetry": "дані про використання телеметрія статистика аналітика",
"crash": "звіти про збої звіти про помилки збій",
"threads": "ланцюжки розмови групувати threads",
"block_images": "блокувати зображення зовнішні зображення завантажувати картинки",
"dark_mode": "темний режим світлий режим нічний режим темна тема",
"timezone": "часовий пояс timezone",
"reading_pane": "область читання область перегляду розділений вигляд",
"week_start": "початок тижня перший день понеділок неділя",
"empty_trash": "очистити кошик кошик видалені",
"do_not_disturb": "тихі години без звуку тиша"
},
"desktop_notifications": {
"title": "На цьому комп’ютері",
"closed_label": "Коли вікно закрите",
"closed_description": "{appName} повідомляє про нову пошту, поки працює. Щоб після закриття вікна він працював далі в треї, увімкніть це в розділі «{page}»."
},
"push_unsupported": "Цей браузер не може показувати сповіщення, коли сайт закритий.",
"backup_description_app": "Однакові налаштування на всіх ваших пристроях.",
"sender_favicons": {
"label": "Зображення з сайтів компаній",
"description": "Показувати піктограми веб-сайтів як зображення профілю для бізнес-відправників"
},
"privacy_push": {
"label": "Ретранслятор сповіщень",
"description": "Сповіщення при закритому {appName} йдуть через push-ретранслятор, який ніколи не бачить вашої пошти. Їх вмикають і вимикають на сторінці «{page}»."
},
"reset": {
"message": "Повернути всі налаштування до початкового стану. Ваша пошта, фільтри, автовідповідь і підписи залишаться як є.",
"sync_on": "Ваші інші пристрої використовують ті самі налаштування, тож вони теж зміняться.",
"sync_off": "Ваші інші пристрої збережуть свої налаштування."
}
},
"setup": {
"title": "Налаштування Bulwark Webmail",
"subtitle": "Налаштуйте свій екземпляр вебпошти в браузері.",
+42
View File
@@ -3517,6 +3517,48 @@
"description": "Đăng xuất tài khoản này trên thiết bị này."
}
},
"uxp2_settings": {
"search": {
"tray": "khay hệ thống vùng thông báo chạy nền thu nhỏ xuống khay tray",
"autostart": "tự khởi động khởi động cùng Windows khi đăng nhập",
"default_app": "ứng dụng thư mặc định chương trình email mặc định mailto liên kết email",
"menu_bar": "thanh menu menu phím Alt Tệp Sửa Xem",
"updates": "cập nhật phiên bản mới beta kênh",
"devtools": "công cụ nhà phát triển devtools bảng điều khiển kiểm tra F12 gỡ lỗi",
"taskbar": "thanh tác vụ nhấp nháy Dock chú ý",
"quiet_hours": "giờ yên lặng không làm phiền tắt tiếng ban đêm",
"telemetry": "thông tin sử dụng đo lường từ xa thống kê phân tích",
"crash": "báo cáo sự cố báo cáo lỗi sự cố",
"threads": "chuỗi thư hội thoại nhóm threads",
"block_images": "chặn hình ảnh hình ảnh bên ngoài tải hình ảnh",
"dark_mode": "chế độ tối chế độ sáng chế độ ban đêm dark mode",
"timezone": "múi giờ timezone",
"reading_pane": "ngăn đọc ngăn xem trước chia đôi màn hình",
"week_start": "đầu tuần ngày đầu tiên thứ Hai Chủ nhật",
"empty_trash": "dọn sạch thùng rác thùng rác mục đã xóa",
"do_not_disturb": "giờ yên lặng tắt tiếng im lặng"
},
"desktop_notifications": {
"title": "Trên máy tính này",
"closed_label": "Khi đóng cửa sổ",
"closed_description": "{appName} báo cho bạn thư mới khi ứng dụng đang chạy. Để ứng dụng tiếp tục chạy ở khay hệ thống sau khi đóng cửa sổ, hãy bật mục đó trong {page}."
},
"push_unsupported": "Trình duyệt này không thể hiển thị thông báo khi trang web đã đóng.",
"backup_description_app": "Dùng cùng cài đặt trên các thiết bị khác của bạn.",
"sender_favicons": {
"label": "Hình ảnh từ trang web của công ty",
"description": "Với người gửi là công ty, dùng biểu tượng trang web làm ảnh người gửi"
},
"privacy_push": {
"label": "Máy chuyển tiếp thông báo",
"description": "Thông báo khi {appName} đó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 bật hoặc tắt chúng trên trang {page}."
},
"reset": {
"message": "Đưa tất cả cài đặt về như lúc ban đầu. Thư, bộ lọc, trả lời tự động và chữ ký của bạn vẫn giữ nguyên.",
"sync_on": "Các thiết bị khác của bạn dùng cùng cài đặt, nên cũng thay đổi theo.",
"sync_off": "Các thiết bị khác của bạn giữ nguyên cài đặt."
}
},
"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.",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "在此裝置上登出此帳號。"
}
},
"uxp2_settings": {
"search": {
"tray": "系統匣 通知區域 背景 最小化到系統匣 tray",
"autostart": "自動啟動 開機啟動 隨 Windows 啟動 登入時啟動",
"default_app": "預設郵件應用程式 預設郵件程式 mailto 郵件連結",
"menu_bar": "功能表列 功能表 Alt 鍵 檔案 編輯 檢視",
"updates": "更新 新版本 測試版 頻道",
"devtools": "開發人員工具 devtools 主控台 檢查 F12 偵錯",
"taskbar": "工作列 閃爍 Dock 提醒",
"quiet_hours": "安靜時段 勿擾 靜音 夜間",
"telemetry": "使用資訊 遙測 統計 分析",
"crash": "當機報告 錯誤報告 當機",
"threads": "會話 對話 分組 討論串 threads",
"block_images": "封鎖圖片 外部圖片 載入圖片",
"dark_mode": "深色模式 淺色模式 夜間模式 暗色 dark mode",
"timezone": "時區 timezone",
"reading_pane": "閱讀窗格 預覽窗格 分割檢視",
"week_start": "每週開始 第一天 星期一 星期日",
"empty_trash": "清空垃圾桶 垃圾桶 已刪除",
"do_not_disturb": "安靜時段 靜音 安靜"
},
"desktop_notifications": {
"title": "在這台電腦上",
"closed_label": "關閉視窗後",
"closed_description": "{appName} 執行期間會提醒你有新郵件。若要在關閉視窗後繼續在系統匣中執行,請在「{page}」中開啟此項。"
},
"push_unsupported": "網站關閉時,此瀏覽器無法顯示通知。",
"backup_description_app": "在你的其他裝置上使用相同的設定。",
"sender_favicons": {
"label": "公司網站的圖片",
"description": "使用企業寄件者的網站圖示作為頭像"
},
"privacy_push": {
"label": "通知中繼",
"description": "{appName} 關閉時的通知會經過推播中繼,中繼永遠看不到你的郵件。可在「{page}」頁面開啟或關閉。"
},
"reset": {
"message": "將所有設定還原為最初的狀態。你的郵件、篩選規則、自動回覆和簽名保持不變。",
"sync_on": "你的其他裝置使用相同的設定,因此也會一起變更。",
"sync_off": "你的其他裝置會保留各自的設定。"
}
},
"setup": {
"title": "Bulwark Webmail 設定",
"subtitle": "在瀏覽器中設定你的網頁郵件執行個體。",
+42
View File
@@ -3505,6 +3505,48 @@
"description": "在此设备上退出此账户。"
}
},
"uxp2_settings": {
"search": {
"tray": "托盘 系统托盘 通知区域 后台 最小化到托盘 tray",
"autostart": "自动启动 开机启动 随 Windows 启动 登录时启动",
"default_app": "默认邮件应用 默认邮件程序 mailto 邮件链接",
"menu_bar": "菜单栏 菜单 Alt 键 文件 编辑 视图",
"updates": "更新 新版本 测试版 渠道",
"devtools": "开发者工具 devtools 控制台 检查 F12 调试",
"taskbar": "任务栏 闪烁 Dock 提醒",
"quiet_hours": "安静时段 勿扰 静音 夜间",
"telemetry": "使用信息 遥测 统计 分析",
"crash": "崩溃报告 错误报告 崩溃",
"threads": "会话 对话 分组 线程 threads",
"block_images": "阻止图片 外部图片 加载图片",
"dark_mode": "深色模式 浅色模式 夜间模式 暗色 dark mode",
"timezone": "时区 timezone",
"reading_pane": "阅读窗格 预览窗格 分栏视图",
"week_start": "每周开始 第一天 星期一 星期日",
"empty_trash": "清空废纸篓 清空回收站 已删除",
"do_not_disturb": "安静时段 静音 安静"
},
"desktop_notifications": {
"title": "在这台电脑上",
"closed_label": "关闭窗口后",
"closed_description": "{appName} 运行期间会提醒你有新邮件。要在关闭窗口后继续在托盘中运行,请在“{page}”中打开此项。"
},
"push_unsupported": "网站关闭时,此浏览器无法显示通知。",
"backup_description_app": "在你的其他设备上使用相同的设置。",
"sender_favicons": {
"label": "公司网站的图片",
"description": "使用企业发件人的网站图标作为头像"
},
"privacy_push": {
"label": "通知中继",
"description": "{appName} 关闭时的通知通过推送中继发送,中继永远看不到你的邮件。可在“{page}”页面打开或关闭。"
},
"reset": {
"message": "将所有设置恢复到最初的状态。你的邮件、过滤规则、自动回复和签名保持不变。",
"sync_on": "你的其他设备使用相同的设置,因此也会一起改变。",
"sync_off": "你的其他设备保留各自的设置。"
}
},
"setup": {
"title": "Bulwark Webmail 设置",
"subtitle": "在浏览器中配置你的网页邮箱实例。",
+4 -1
View File
@@ -1592,7 +1592,10 @@ export const useSettingsStore = create<SettingsState>()(
},
resetToDefaults: () => {
set(DEFAULT_SETTINGS);
// The choice to keep settings in sync stays: the confirm says other
// devices change only when sync is on (settings-p2-18), and a reset
// turning sync back on would push these defaults to all of them.
set({ ...DEFAULT_SETTINGS, settingsSyncDisabled: get().settingsSyncDisabled });
// The defaults of this instance are the operator's, then the build's.
applyAdminPolicyPatch();
applyVisualSettings();