mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-05 00:14:18 +00:00
Merge branch 'feat/do-ux-p2-settings-search' into feat/do-ux-p2-settings-fix
This commit is contained in:
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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")}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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')}>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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
@@ -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('.')) {
|
||||
|
||||
@@ -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": "اضبط نسخة البريد الإلكتروني على الويب من المتصفح.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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": "نمونهٔ وبمیل خود را از مرورگر پیکربندی کنید.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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": "הגדירו את מופע דואר הרשת שלכם מהדפדפן.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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 メールのインスタンスを設定します。",
|
||||
|
||||
@@ -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": "브라우저에서 웹메일 인스턴스를 설정하세요.",
|
||||
|
||||
@@ -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ā.",
|
||||
|
||||
@@ -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": "Вэб шуудангийн инстансаа хөтчөөс тохируулна уу.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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": "Настройте свой экземпляр веб-почты в браузере.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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": "ตั้งค่าอินสแตนซ์เว็บเมลของคุณจากเบราว์เซอร์",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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": "Налаштуйте свій екземпляр вебпошти в браузері.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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": "在瀏覽器中設定你的網頁郵件執行個體。",
|
||||
|
||||
@@ -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": "在浏览器中配置你的网页邮箱实例。",
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user