fix: keep sender and subject off the OS until they're known to be allowed, leave nothing on it after sign-out, and take files only where they're meant

This commit is contained in:
Linus Rath
2026-11-03 12:24:14 +01:00
parent f14116f128
commit 41fcbba61c
47 changed files with 1277 additions and 51 deletions
+7 -3
View File
@@ -4,7 +4,8 @@ import { useEffect, useRef } from "react";
import { useLocale, useTranslations } from "next-intl";
import { useArchiveActionEnabled } from "@/lib/archive-gate";
import { getDesktopBridge } from "@/lib/desktop/bridge";
import { startMailNotifications, type MailNotificationLabels, type MailNotifications } from "@/lib/desktop/mail-notifications";
import { isAppLocked } from "@/lib/desktop/app-lock-state";
import { contentHiddenFor, startMailNotifications, type MailNotificationLabels, type MailNotifications } from "@/lib/desktop/mail-notifications";
import { startOsSummary } from "@/lib/desktop/os-summary";
import { startPowerWatch } from "@/lib/desktop/power";
import { startWindowFiles } from "@/lib/desktop/window-files";
@@ -49,7 +50,10 @@ export function DesktopOsHost() {
labelsRef.current = labels;
useEffect(() => {
if (!getDesktopBridge()?.os) return;
const os = getDesktopBridge()?.os;
if (!os) return;
// Main shows no content anywhere until this page has said it may.
os.setLocked(isAppLocked());
const mail = startMailNotifications(labelsRef.current);
mailRef.current = mail;
const summary = startOsSummary(() => localeRef.current);
@@ -64,7 +68,7 @@ export function DesktopOsHost() {
const event = "eventId" in view;
void getDesktopBridge()?.os?.notify({
id: `reminder:${event ? view.eventId : view.taskId}:${view.id}:${view.triggerTime}`,
title: (event ? view.eventTitle : view.taskTitle) || tCalendar("alert_title"),
title: (contentHiddenFor(useAuthStore.getState().activeAccountId) ? "" : event ? view.eventTitle : view.taskTitle) || tCalendar("alert_title"),
body: "",
context: "{}",
});
+5 -2
View File
@@ -2650,8 +2650,11 @@ export function EmailComposer({
resetDragTimeout();
}, [resetDragTimeout]);
// The desktop app: files dropped or pasted anywhere on the window come here.
useEffect(() => setComposerFileTarget((files) => void addFiles(files)), [addFiles]);
// The desktop app: files dropped or pasted anywhere on the window come here, if this one's on screen.
useEffect(() => setComposerFileTarget(
(files) => void addFiles(files),
() => !!composerRootEl?.isConnected && composerRootEl.getClientRects().length > 0,
), [addFiles, composerRootEl]);
const handleDrop = useCallback((e: React.DragEvent) => {
e.preventDefault();
+13 -9
View File
@@ -685,15 +685,19 @@ function DraggableAttachmentChip({ attachment, client, accountId, enabled, downl
const source = useMemo<AttachmentDragSource>(() => ({
name: downloadName || attachment.name || 'download',
type: attachment.type || 'application/octet-stream',
getBlob: async () => {
if (attachment.blobId && client) {
return client.fetchBlob(attachment.blobId, downloadName || attachment.name || undefined, attachment.type, accountId).catch(() => null);
}
const bytes = attachment.tnefData ?? (attachment.decryptedAttachment ? getAttachmentContentBytes(attachment.decryptedAttachment) : null);
if (!bytes || bytes.byteLength === 0) return null;
const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;
return new Blob([buffer], { type: inertBlobType(attachment.type) });
},
// A decrypted attachment doesn't go through the app's temporary folder or the clipboard: it
// stays with the browser's drag, which only writes it where the user drops it.
...(attachment.decryptedAttachment ? {} : {
getBlob: async () => {
if (attachment.blobId && client) {
return client.fetchBlob(attachment.blobId, downloadName || attachment.name || undefined, attachment.type, accountId).catch(() => null);
}
const bytes = attachment.tnefData;
if (!bytes || bytes.byteLength === 0) return null;
const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;
return new Blob([buffer], { type: inertBlobType(attachment.type) });
},
}),
getBlobUrl: async () => {
if (attachment.blobId && client) {
try {
@@ -111,6 +111,7 @@ export function ProtocolLaunchHandlerProvider({ children }: ProtocolLaunchHandle
const router = useRouter();
const pathname = usePathname();
const tLaunch = useTranslations("pwaLaunch");
const tDesktop = useTranslations("desktopOs");
const sentToTarget = useRef(new Set<string>());
const { dialogProps, confirm } = useConfirmDialog();
const [offer, setOffer] = useState<ShareOffer | null>(null);
@@ -213,6 +214,7 @@ export function ProtocolLaunchHandlerProvider({ children }: ProtocolLaunchHandle
if (osEnv.current.pathname !== path) osEnv.current.push(path);
},
notifyFailed: () => toast.error(tLaunch("open_failed")),
notifyReplyLost: () => toast.error(tDesktop("reply_lost")),
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
+29 -1
View File
@@ -33,7 +33,8 @@ async function fingerprint() {
const leaveFor = vi.fn();
const push = vi.fn();
const notifyFailed = vi.fn();
const env = { leaveFor, push, notifyFailed };
const notifyReplyLost = vi.fn();
const env = { leaveFor, push, notifyFailed, notifyReplyLost };
const focus = vi.fn();
async function run(command: DesktopOsCommand) {
@@ -191,6 +192,33 @@ describe('a toast\'s buttons', () => {
stop();
});
it('behind the app lock an old toast acts on nothing, and the window comes up', async () => {
const { setAppLockCheck } = await import('../app-lock-state');
const { setMailLinkOpener } = await import('../mail-links');
const opener = vi.fn();
const stop = setMailLinkOpener(opener);
setAppLockCheck(() => true);
for (const action of ['delete', 'archive', 'mark-read', 'open', 'reply']) {
await run({ type: 'notification', action, context: await context(), reply: 'x' });
}
expect(runNotificationAction).not.toHaveBeenCalled();
expect(opener).not.toHaveBeenCalled();
expect(focus).toHaveBeenCalled();
setAppLockCheck(null);
stop();
});
it('typed text that has nowhere to go says so', async () => {
const { setMailLinkOpener } = await import('../mail-links');
const stop = setMailLinkOpener(vi.fn());
await run({ type: 'notification', action: 'reply', context: await context({ login: 'someone-else' }), reply: 'On my way' });
expect(notifyReplyLost).toHaveBeenCalled();
notifyReplyLost.mockClear();
await run({ type: 'notification', action: 'reply', context: await context({ login: 'someone-else' }) });
expect(notifyReplyLost).not.toHaveBeenCalled();
stop();
});
it('a click on an old toast whose slot went to someone else opens the Inbox, not their message', async () => {
const { setMailLinkOpener } = await import('../mail-links');
const opener = vi.fn();
+11
View File
@@ -87,6 +87,8 @@ beforeEach(async () => {
});
const { setAppLockCheck } = await import('../app-lock-state');
setAppLockCheck(null);
const { usePolicyStore } = await import('@/stores/policy-store');
usePolicyStore.setState({ loaded: true });
});
afterEach(() => {
@@ -266,6 +268,15 @@ describe('what the notification says', () => {
expect(JSON.stringify(notification)).not.toContain('Alice Sender');
});
it('before the policy is known: the same', async () => {
const { buildMailNotification } = await import('../mail-notifications');
const { usePolicyStore } = await import('@/stores/policy-store');
usePolicyStore.setState({ loaded: false });
const notification = await buildMailNotification([one()], labels);
expect(JSON.stringify(notification)).not.toContain('Secret');
expect(JSON.stringify(notification)).not.toContain('Alice Sender');
});
it('several at once are told about once, and that one has no message to act on', async () => {
const { buildMailNotification } = await import('../mail-notifications');
const notification = await buildMailNotification([one('a'), one('b'), one('c')], labels);
+11
View File
@@ -49,6 +49,17 @@ describe('the store and the OS integration', () => {
expect(listener).toHaveBeenCalledWith(client, 'acc', ['e1', 'e2']);
});
it('a wipe clears what the OS shows, and a browser has nothing to clear', async () => {
const { clearDesktopSurfaces } = await import('../bridge');
expect(() => clearDesktopSurfaces()).not.toThrow();
const clearSurfaces = vi.fn();
window.bulwarkDesktop = { version: 1, platform: 'win32', os: { clearSurfaces } } as never;
const { wipeAccount } = await import('@/lib/offline/offline-mode');
await wipeAccount('alice@one').catch(() => {});
expect(clearSurfaces).toHaveBeenCalled();
window.bulwarkDesktop = undefined;
});
it('other collections are not mail', async () => {
const { setStoreCreatedListener } = await import('../system-events');
const listener = vi.fn();
+161 -7
View File
@@ -159,7 +159,7 @@ describe('files on the window', () => {
setBridge({});
const stop = startWindowFiles();
const target = vi.fn();
const clear = setComposerFileTarget(target);
const clear = setComposerFileTarget(target, () => true);
window.dispatchEvent(dropEvent('drop', [file]));
expect(target).toHaveBeenCalledWith([file]);
expect(takeHandoff('compose')).toBeNull();
@@ -167,6 +167,20 @@ describe('files on the window', () => {
stop();
});
it('a composer out of sight on a hidden tab takes nothing', async () => {
const { setComposerFileTarget, startWindowFiles } = await import('../window-files');
const { takeHandoff } = await import('@/lib/launch-handoff');
setBridge({});
const stop = startWindowFiles();
const hidden = vi.fn();
const clear = setComposerFileTarget(hidden, () => false);
window.dispatchEvent(dropEvent('drop', [file]));
expect(hidden).not.toHaveBeenCalled();
expect(takeHandoff('compose')?.files).toEqual([file]);
clear();
stop();
});
it('a pasted file is taken; pasted text is not', async () => {
const { startWindowFiles } = await import('../window-files');
const { takeHandoff } = await import('@/lib/launch-handoff');
@@ -174,7 +188,7 @@ describe('files on the window', () => {
const stop = startWindowFiles();
const paste = (files: File[]) => {
const event = new Event('paste', { bubbles: true, cancelable: true });
Object.defineProperty(event, 'clipboardData', { value: { files } });
Object.defineProperty(event, 'clipboardData', { value: { files, types: files.length ? ['Files'] : ['text/plain'] } });
window.dispatchEvent(event);
return event;
};
@@ -185,6 +199,54 @@ describe('files on the window', () => {
stop();
});
describe('a paste that is not for us', () => {
const pasteOn = (target: EventTarget, types: string[]) => {
const event = new Event('paste', { bubbles: true, cancelable: true });
Object.defineProperty(event, 'clipboardData', { value: { files: [file], types } });
target.dispatchEvent(event);
return event;
};
it('text with a file, as a spreadsheet copies, stays text', async () => {
const { startWindowFiles } = await import('../window-files');
const { takeHandoff } = await import('@/lib/launch-handoff');
setBridge({});
const stop = startWindowFiles();
expect(pasteOn(document.body, ['text/plain', 'Files']).defaultPrevented).toBe(false);
expect(takeHandoff('compose')).toBeNull();
stop();
});
it('in a search field or any other input, a file is not an attachment', async () => {
const { startWindowFiles } = await import('../window-files');
const { takeHandoff } = await import('@/lib/launch-handoff');
setBridge({});
const stop = startWindowFiles();
const input = document.body.appendChild(document.createElement('input'));
expect(pasteOn(input, ['Files']).defaultPrevented).toBe(false);
expect(takeHandoff('compose')).toBeNull();
input.remove();
stop();
});
it('in the body of a composer on screen, a bare file attaches', async () => {
const { setComposerFileTarget, startWindowFiles } = await import('../window-files');
setBridge({});
const stop = startWindowFiles();
const target = vi.fn();
const clear = setComposerFileTarget(target, () => true);
const composer = document.body.appendChild(document.createElement('div'));
composer.setAttribute('data-testid', 'email-composer');
const body = composer.appendChild(document.createElement('div'));
body.setAttribute('contenteditable', 'true');
pasteOn(body, ['Files']);
expect(target).toHaveBeenCalledWith([file]);
composer.remove();
clear();
stop();
});
});
it('our own native drag over our own window is not a new file', async () => {
const { setOwnDragActive, startWindowFiles } = await import('../window-files');
const { takeHandoff } = await import('@/lib/launch-handoff');
@@ -247,6 +309,23 @@ describe('dragging an attachment out', () => {
await waitFor(() => expect(startDrag).toHaveBeenCalled());
});
it('in the app, without getBlob (a decrypted attachment): the browser drag, and no copy button', async () => {
const startDrag = vi.fn(async () => true);
setBridge({ startDrag, copyFiles: vi.fn() as never });
const { useAttachmentDrag } = await import('@/hooks/use-attachment-drag');
const { name, type, getBlobUrl } = source;
const hook = renderHook(() => useAttachmentDrag({ name, type, getBlobUrl }, true));
act(() => hook.result.current.onPointerEnter());
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 0));
});
const event = { preventDefault: vi.fn(), dataTransfer: { setData: vi.fn(), effectAllowed: '' } };
hook.result.current.onDragStart(event as never);
expect(startDrag).not.toHaveBeenCalled();
expect(event.dataTransfer.setData).toHaveBeenCalledWith('DownloadURL', expect.stringContaining(blobUrl));
expect(hook.result.current.onCopyAsFile).toBeUndefined();
});
it('in a browser: still DownloadURL, and no copy button', async () => {
const hook = await ready();
const event = { preventDefault: vi.fn(), dataTransfer: { setData: vi.fn(), effectAllowed: '' } };
@@ -313,15 +392,52 @@ describe('waking up', () => {
stop();
});
it('a network change counts as waking', async () => {
const connection = new EventTarget();
vi.stubGlobal('navigator', Object.assign(Object.create(navigator), { connection }));
it('a new kind of network counts as waking; a change in its speed estimate does not', async () => {
const connection = Object.assign(new EventTarget(), { type: 'wifi' });
vi.stubGlobal('navigator', Object.create(navigator, { connection: { value: connection }, onLine: { value: true } }));
const { power, stop } = await started();
const wake = vi.fn();
const stopWake = power.addWakeHandler(wake);
await new Promise((resolve) => setTimeout(resolve, 0));
connection.dispatchEvent(new Event('change'));
expect(wake).toHaveBeenCalled();
expect(wake).not.toHaveBeenCalled();
connection.type = 'ethernet';
connection.dispatchEvent(new Event('change'));
expect(wake).toHaveBeenCalledTimes(1);
stopWake();
stop();
});
it('where Chromium gives no type, a change is nothing to go on', async () => {
const connection = new EventTarget();
vi.stubGlobal('navigator', Object.create(navigator, { connection: { value: connection }, onLine: { value: true } }));
const { power, stop } = await started();
const wake = vi.fn();
const stopWake = power.addWakeHandler(wake);
connection.dispatchEvent(new Event('change'));
expect(wake).not.toHaveBeenCalled();
stopWake();
stop();
});
it('woken up with no network, it does not say the network is back, and a wake within 30 s is the same wake', async () => {
const online = vi.fn();
window.addEventListener('online', online);
vi.stubGlobal('navigator', Object.create(navigator, { onLine: { value: false } }));
const { fire, power, stop } = await started();
const wake = vi.fn();
const stopWake = power.addWakeHandler(wake);
fire('resume');
expect(wake).toHaveBeenCalledTimes(1);
expect(online).not.toHaveBeenCalled();
const now = Date.now();
const clock = vi.spyOn(Date, 'now').mockReturnValue(now + 29_000);
fire('unlock-screen');
expect(wake).toHaveBeenCalledTimes(1);
clock.mockReturnValue(now + 31_000);
fire('unlock-screen');
expect(wake).toHaveBeenCalledTimes(2);
clock.mockRestore();
window.removeEventListener('online', online);
stopWake();
stop();
});
@@ -357,6 +473,8 @@ describe('the summary', () => {
useAuthStore.setState({ isAuthenticated: true, client: fake as never, activeAccountId: 'alice@one' });
useAccountStore.setState({ accounts: [{ id: 'alice@one', cookieSlot: 3, serverUrl: 'https://one.example', username: 'alice@example.org' }] as never });
useEmailStore.setState({ mailboxes: [{ id: 'in', role: 'inbox', name: 'Inbox' }] as never });
const { usePolicyStore } = await import('@/stores/policy-store');
usePolicyStore.setState({ loaded: true });
});
afterEach(async () => {
@@ -374,6 +492,42 @@ describe('the summary', () => {
expect(getEmails).toHaveBeenCalledWith('in', undefined, 5, 0, undefined, false, { notKeyword: '$seen' });
});
it('before the policy is known it is empty', async () => {
const { usePolicyStore } = await import('@/stores/policy-store');
const { readSummary, EMPTY_SUMMARY } = await import('../os-summary');
usePolicyStore.setState({ loaded: false });
await expect(readSummary('en')).resolves.toEqual(EMPTY_SUMMARY);
expect(getEmails).not.toHaveBeenCalled();
});
it('a read under way when the login goes doesn\'t bring it back; a policy change reads again', async () => {
const setSummary = vi.fn();
setBridge({ setSummary });
const { startOsSummary, EMPTY_SUMMARY } = await import('../os-summary');
const { useAuthStore } = await import('@/stores/auth-store');
const { usePolicyStore } = await import('@/stores/policy-store');
let answer: (value: unknown) => void = () => {};
getEmails.mockImplementationOnce(() => new Promise((resolve) => { answer = resolve as never; }) as never);
vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval'] });
const summary = startOsSummary(() => 'en');
await vi.advanceTimersByTimeAsync(2100);
useAuthStore.setState({ isAuthenticated: false, client: null, activeAccountId: null });
expect(setSummary).toHaveBeenLastCalledWith(EMPTY_SUMMARY);
answer({ emails: [row], hasMore: false, total: 1 });
await vi.advanceTimersByTimeAsync(10);
await new Promise((resolve) => setImmediate(resolve));
await vi.advanceTimersByTimeAsync(2100);
expect(setSummary).toHaveBeenLastCalledWith(EMPTY_SUMMARY);
expect(JSON.stringify(setSummary.mock.calls)).not.toContain('Lunch');
setSummary.mockClear();
usePolicyStore.setState({ policy: { ...usePolicyStore.getState().policy } });
await vi.advanceTimersByTimeAsync(2100);
expect(setSummary).toHaveBeenCalled();
summary.stop();
vi.useRealTimers();
});
it('while locked it is empty, without asking anyone anything', async () => {
const { setAppLockCheck } = await import('../app-lock-state');
const { readSummary, EMPTY_SUMMARY } = await import('../os-summary');
+5
View File
@@ -19,6 +19,11 @@ export function forgetDesktopServer(serverUrl: string, reason: 'failed' | 'signe
getDesktopBridge()?.servers?.forget?.(serverUrl, reason);
}
/** Nothing of an account stays on the OS's surfaces after a sign-out or a wipe. */
export function clearDesktopSurfaces(): void {
getDesktopBridge()?.os?.clearSurfaces?.();
}
/** True in any window of the desktop app, standalone or on an instance. */
export function isDesktopApp(): boolean {
return getDesktopBridge() !== null;
+9 -3
View File
@@ -21,6 +21,7 @@ import { isPopoutWindow } from '@/lib/popout/window-role';
import { loginFingerprint } from '@/lib/pwa-notify/login-fingerprint';
import { useAccountStore } from '@/stores/account-store';
import { useAuthStore } from '@/stores/auth-store';
import { usePolicyStore } from '@/stores/policy-store';
import { useSettingsStore } from '@/stores/settings-store';
import type { IJMAPClient } from '@/lib/jmap/client-interface';
@@ -72,17 +73,22 @@ async function senderInfo(client: IJMAPClient): Promise<Sender> {
};
}
function withoutContent(sender: Sender): boolean {
/** Sender and subject stay off everything outside the app. Not knowing yet counts as yes. */
export function contentHiddenFor(accountEntryId: string | null | undefined): boolean {
if (isAppLocked()) return true;
// Unknown counts as on, as on the server's route.
if (!sender.entryId) return true;
if (!accountEntryId || !usePolicyStore.getState().loaded) return true;
try {
return devicesForAccount(sender.entryId).pushWithoutContent;
return devicesForAccount(accountEntryId).pushWithoutContent;
} catch {
return true;
}
}
function withoutContent(sender: Sender): boolean {
return contentHiddenFor(sender.entryId);
}
function senderName(mail: NewMail): string {
const first = mail.email.from?.[0];
return first?.name || first?.email || '';
+9
View File
@@ -5,6 +5,7 @@
* user's own OS, so it skips the question a web share gets (decision 17).
*/
import { isAppLocked } from '@/lib/desktop/app-lock-state';
import { getDesktopBridge } from '@/lib/desktop/bridge';
import type { DesktopOsCommand } from '@/lib/desktop/types';
import { openMailLink } from '@/lib/desktop/mail-links';
@@ -25,6 +26,8 @@ export interface OsCommandEnv {
push: (path: string) => void;
/** Says something couldn't be opened. */
notifyFailed: (names: readonly string[]) => void;
/** A typed reply that has nowhere to go: its message's login is gone from here. */
notifyReplyLost: () => void;
}
/** How long a toast's button waits for a cold start to sign in. */
@@ -77,6 +80,7 @@ async function openFromNotification(context: MailNotificationContext, env: OsCom
openMailLink({ kind: 'message', id: context.emailId, slot: context.slot, ownerAccountId: context.accountId, ...(reply ? { reply: true } : {}) }, reply ? { replyText: reply.text } : undefined, env.leaveFor);
return;
}
if (reply?.text) env.notifyReplyLost();
openMailLink({ kind: 'folder', ref: 'inbox', ...(known ? { slot: context.slot } : {}) }, undefined, env.leaveFor);
}
@@ -89,6 +93,11 @@ function runAction(request: NotificationActionRequest): Promise<NotificationActi
}
async function handleNotification(action: string, rawContext: string, reply: string | undefined, env: OsCommandEnv): Promise<void> {
// An old toast's Delete doesn't act behind the lock, and its message doesn't open over it.
if (isAppLocked()) {
showWindow();
return;
}
const context = parseContext(rawContext);
if (action === 'reply') {
await openFromNotification(context, env, { text: reply ?? '' });
+17 -9
View File
@@ -6,9 +6,8 @@
*/
import { getDesktopBridge } from '@/lib/desktop/bridge';
import { isAppLocked } from '@/lib/desktop/app-lock-state';
import { contentHiddenFor } from '@/lib/desktop/mail-notifications';
import type { DesktopOsSummary, DesktopOsTrayMessage } from '@/lib/desktop/types';
import { devicesForAccount } from '@/lib/device-policy';
import type { CalendarEvent, Email } from '@/lib/jmap/types';
import type { IJMAPClient } from '@/lib/jmap/client-interface';
import { loginFingerprint } from '@/lib/pwa-notify/login-fingerprint';
@@ -16,6 +15,7 @@ import { useAccountStore } from '@/stores/account-store';
import { useAuthStore } from '@/stores/auth-store';
import { useCalendarStore } from '@/stores/calendar-store';
import { useEmailStore } from '@/stores/email-store';
import { usePolicyStore } from '@/stores/policy-store';
const ROWS = 5;
const REFRESH_DELAY_MS = 2000;
@@ -46,12 +46,10 @@ export function formatEventTime(start: number, now: number, locale: string | und
}
export async function readSummary(locale: string | undefined, now = Date.now()): Promise<DesktopOsSummary> {
if (isAppLocked()) return EMPTY_SUMMARY;
const auth = useAuthStore.getState();
const client: IJMAPClient | null = auth.client;
const entry = useAccountStore.getState().accounts.find((account) => account.id === auth.activeAccountId);
if (!auth.isAuthenticated || !client || !entry) return EMPTY_SUMMARY;
if (devicesForAccount(entry.id).pushWithoutContent) return EMPTY_SUMMARY;
if (!auth.isAuthenticated || !client || !entry || contentHiddenFor(entry.id)) return EMPTY_SUMMARY;
const inbox = useEmailStore.getState().mailboxes.find((mailbox) => mailbox.role === 'inbox' && !mailbox.isShared);
let unread: DesktopOsTrayMessage[] = [];
@@ -91,12 +89,14 @@ export function startOsSummary(locale: () => string | undefined): { refresh: ()
const os = getDesktopBridge()?.os;
if (!os) return { refresh: () => {}, stop: () => {} };
let timer: ReturnType<typeof setTimeout> | null = null;
let stale = false;
/** A read under way when the account changed answers for the old one, and is dropped. */
let generation = 0;
const run = () => {
timer = null;
const mine = ++generation;
void readSummary(locale()).then((summary) => {
if (!stale) os.setSummary(summary);
if (mine === generation) os.setSummary(summary);
}, (error) => {
console.warn('Could not read the tray summary:', error);
});
@@ -113,16 +113,24 @@ export function startOsSummary(locale: () => string | undefined): { refresh: ()
refresh();
});
const stopAuth = useAuthStore.subscribe((state, previous) => {
if (state.activeAccountId !== previous.activeAccountId || state.isAuthenticated !== previous.isAuthenticated) refresh();
if (state.activeAccountId === previous.activeAccountId && state.isAuthenticated === previous.isAuthenticated) return;
generation += 1;
if (!state.isAuthenticated) os.setSummary(EMPTY_SUMMARY);
refresh();
});
// An organisation turning sender and subject off goes for what's already showing, too.
const stopPolicy = usePolicyStore.subscribe((state, previous) => {
if (state.loaded !== previous.loaded || state.policy !== previous.policy) refresh();
});
const interval = setInterval(refresh, REFRESH_EVERY_MS);
refresh();
return {
refresh,
stop() {
stale = true;
generation += 1;
stopEmail();
stopAuth();
stopPolicy();
clearInterval(interval);
if (timer) clearTimeout(timer);
},
+17 -7
View File
@@ -13,12 +13,13 @@ import { getDesktopBridge } from '@/lib/desktop/bridge';
import { runSleepHandlers, runWakeHandlers } from '@/lib/desktop/system-events';
import { usePolicyStore } from '@/stores/policy-store';
/** A resume and an unlock usually come together. */
const WAKE_REPEAT_MS = 2000;
/** A resume, an unlock and a network change come together, and each would mean a round of requests. */
const WAKE_REPEAT_MS = 30_000;
function wake(): void {
document.dispatchEvent(new Event('visibilitychange'));
window.dispatchEvent(new Event('online'));
// Woken up with no network: the browser's own event comes when there is one.
if (navigator.onLine !== false) window.dispatchEvent(new Event('online'));
const policy = usePolicyStore.getState();
if (policy.loaded && !policy.fetching) void policy.fetchPolicy();
runWakeHandlers();
@@ -38,11 +39,20 @@ export function startPowerWatch(): () => void {
if (event === 'resume' || event === 'unlock-screen') wakeOnce();
else if (event === 'suspend') runSleepHandlers();
});
// Wi-Fi to cable is a new network with no offline moment in between.
const connection = (navigator as unknown as { connection?: EventTarget }).connection;
connection?.addEventListener?.('change', wakeOnce);
// Wi-Fi to cable is a new network with no offline moment in between. This event also fires
// for every shift in its speed estimate, which isn't news. Where Chromium doesn't report the
// type, that leaves the stream's own ping check and the online event.
const connection = (navigator as unknown as { connection?: EventTarget & { type?: string } }).connection;
let lastType = connection?.type;
const onConnection = () => {
const type = connection?.type;
if (!type || type === lastType) return;
lastType = type;
wakeOnce();
};
connection?.addEventListener?.('change', onConnection);
return () => {
stopPower();
connection?.removeEventListener?.('change', wakeOnce);
connection?.removeEventListener?.('change', onConnection);
};
}
+9 -2
View File
@@ -168,7 +168,7 @@ export interface DesktopOsNotification {
export interface DesktopOsNotifyResult {
shown: boolean;
reason?: 'duplicate' | 'paused' | 'quiet-hours' | 'focused' | 'unsupported' | 'invalid';
reason?: 'duplicate' | 'paused' | 'quiet-hours' | 'focused' | 'unsupported' | 'invalid' | 'failed';
/** The page should play the sound the user picked. */
playSound: boolean;
}
@@ -214,8 +214,13 @@ export interface DesktopOsBridge {
setSummary: (summary: DesktopOsSummary) => void;
/** The taskbar's progress bar: lib/desktop/file-drag.ts setTaskbarProgress. */
setProgress: (progress: number | null, mode?: 'normal' | 'indeterminate' | 'paused' | 'error') => void;
/** App lock's state: main clears the tray, jump list and toasts and strips what comes through. */
/**
* App lock's state. Main counts as locked until the main window's page says otherwise,
* and clears the tray, jump list and toasts and strips what comes through.
*/
setLocked: (locked: boolean) => void;
/** Sign-out or a wipe: delivered toasts, badge, tray and jump list go, and temporary files. */
clearSurfaces: () => void;
notify: (notification: DesktopOsNotification) => Promise<DesktopOsNotifyResult>;
onPower: (listener: (event: DesktopOsPowerEvent) => void) => () => void;
/** Writes the files to a private folder and starts a drag. Only with the mouse held. */
@@ -225,6 +230,8 @@ export interface DesktopOsBridge {
defaultAppStatus: () => Promise<DesktopOsDefaultAppStatus>;
/** Offers the app to the OS and opens where the user chooses. */
openDefaultAppSettings: () => Promise<DesktopOsDefaultAppStatus>;
}
export type DesktopKeystoreHash = 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512';
export interface DesktopKeystoreCertificate {
+37 -8
View File
@@ -8,29 +8,58 @@
import { getDesktopBridge } from '@/lib/desktop/bridge';
import { handOffDraft } from '@/lib/launch-handoff';
type ComposerTarget = (files: File[]) => unknown;
interface ComposerTarget {
take: (files: File[]) => unknown;
/** The Pro shell keeps composers mounted on tabs out of sight. */
visible: () => boolean;
}
let composerTarget: ComposerTarget | null = null;
const composerTargets: ComposerTarget[] = [];
/** Our own native drag passing over our own window isn't a new file. */
let ownDrag = false;
/** The open composer takes files itself. No effect outside the desktop app: only this file reads it. */
export function setComposerFileTarget(target: ComposerTarget): () => void {
composerTarget = target;
/** A composer on screen takes files itself. No effect outside the desktop app: only this file reads it. */
export function setComposerFileTarget(take: ComposerTarget['take'], visible: ComposerTarget['visible']): () => void {
const target = { take, visible };
composerTargets.push(target);
return () => {
if (composerTarget === target) composerTarget = null;
const at = composerTargets.indexOf(target);
if (at !== -1) composerTargets.splice(at, 1);
};
}
function visibleComposer(): ComposerTarget | undefined {
return [...composerTargets].reverse().find((target) => target.visible());
}
export function setOwnDragActive(active: boolean): void {
ownDrag = active;
}
function take(files: File[]): void {
if (composerTarget) composerTarget(files);
const composer = visibleComposer();
if (composer) composer.take(files);
else handOffDraft({ files });
}
function isEditable(target: EventTarget | null): boolean {
const element = target as Partial<HTMLElement> & { closest?: (selector: string) => Element | null } | null;
return !!element?.closest?.('input, textarea, select, [contenteditable=""], [contenteditable="true"]');
}
/**
* Pasting a file is only meant where text isn't. A spreadsheet's cells also come as a
* picture of them, and a field that takes text wants the text. The one editable place a
* bare file belongs in is the composer it's being pasted into.
*/
function pasteIsForUs(event: ClipboardEvent): boolean {
const types = [...(event.clipboardData?.types ?? [])];
if (types.includes('text/plain') || types.includes('text/html')) return false;
if (!isEditable(event.target)) return true;
const inComposer = !!(event.target as Partial<Element> | null)?.closest?.('[data-testid="email-composer"]');
return inComposer && !!visibleComposer();
}
function hasFiles(event: DragEvent): boolean {
return !!event.dataTransfer && [...event.dataTransfer.types].includes('Files');
}
@@ -51,7 +80,7 @@ export function startWindowFiles(): () => void {
const onPaste = (event: ClipboardEvent) => {
if (event.defaultPrevented) return;
const files = Array.from(event.clipboardData?.files ?? []);
if (files.length === 0) return;
if (files.length === 0 || !pasteIsForUs(event)) return;
event.preventDefault();
take(files);
};
+3
View File
@@ -62,6 +62,7 @@ import { flushQueue, LOOKUP_WAIT_MS, SENDING_STALE_MS, settleStaleSends } from '
import { LEGACY_DB_NAME, moveLegacyOutbox, strandedLegacyMessages } from './migrate-legacy';
import { deleteShellCaches, tellServiceWorker } from './shell';
import { rememberLost } from './lost';
import { clearDesktopSurfaces } from '@/lib/desktop/bridge';
import { addSleepHandler, reportStoreCreatedMail } from '@/lib/desktop/system-events';
export type OfflineNotice =
@@ -807,6 +808,7 @@ async function nameLost(scope: string, stillSignedIn: boolean): Promise<void> {
}
export async function wipeAccount(account: string): Promise<void> {
clearDesktopSurfaces();
const timer = syncTimers.get(account);
if (timer) clearTimeout(timer);
syncTimers.delete(account);
@@ -838,6 +840,7 @@ export async function wipeAccount(account: string): Promise<void> {
/** Nothing of offline mode stays on this device. */
export async function wipeEverything(): Promise<void> {
clearDesktopSurfaces();
for (const timer of syncTimers.values()) clearTimeout(timer);
syncTimers.clear();
writeEnabled([]);
+2
View File
@@ -1,6 +1,7 @@
import { fileStorage } from '@/lib/plugin-storage';
import { clearHandoffs } from '@/lib/launch-handoff';
import { clearAllCheckpoints } from '@/lib/mail-import/checkpoints';
import { clearDesktopSurfaces } from '@/lib/desktop/bridge';
import { sweepPrivateExports } from '@/lib/mail-export/sinks';
import { purgeStoreFiles } from '@/lib/store/client/purge';
@@ -138,6 +139,7 @@ export function purgeSignedOutData(options: SignOutCleanupOptions = {}): void {
}
// Files and links on their way into a draft or an import.
clearHandoffs();
clearDesktopSurfaces();
for (const cleanup of signedOutCleanups) {
try {
cleanup(options);
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "تم نقل هذه الرسالة إلى {folder}.",
"window_not_ready": "لم تفتح النافذة الجديدة في الوقت المناسب. تبقى مسودتك هنا."
},
"desktopOs": {
"section_title": "تطبيق سطح المكتب",
"section_description": "طريقة عمل التطبيق مع حاسوبك.",
"close_to_tray_label": "الاستمرار في العمل في شريط النظام عند الإغلاق",
"close_to_tray_description": "يستمر وصول البريد الجديد والتذكيرات بعد إغلاق النافذة.",
"start_at_login_label": "البدء عند تسجيل الدخول إلى الحاسوب",
"start_at_login_description": "يبدأ التطبيق تلقائيًا عند تسجيل الدخول.",
"start_minimized_label": "البدء في شريط النظام",
"start_minimized_description": "لا تُفتح نافذة عند تسجيل الدخول. استخدم أيقونة شريط النظام لفتحه.",
"attention_label": "وميض شريط المهام عند وصول بريد جديد",
"attention_mac_label": "قفز أيقونة Dock عند وصول بريد جديد",
"attention_description": "فقط عندما لا يكون التطبيق في المقدمة.",
"quiet_hours_label": "ساعات الهدوء",
"quiet_hours_description": "لا إشعارات ولا أصوات في هذه الأوقات. يستمر وصول البريد.",
"quiet_hours_from": "من",
"quiet_hours_to": "إلى",
"show_shortcut_label": "إظهار التطبيق باختصار لوحة المفاتيح",
"show_shortcut_description": "{keys} يُظهر التطبيق في المقدمة من أي تطبيق آخر.",
"show_shortcut_taken": "يستخدم تطبيق آخر {keys}. أغلق ذلك التطبيق، أو أوقف هذا الخيار ثم شغّله مرة أخرى.",
"default_app_label": "تطبيق البريد الافتراضي",
"default_app_description": "فتح روابط البريد وملفات ‎.eml و‎.ics و‎.vcf بهذا التطبيق.",
"default_app_is_default": "هذا التطبيق يفتح روابط البريد لديك.",
"default_app_button": "اختيار التطبيق الافتراضي…",
"new_mail": "بريد جديد",
"no_subject": "(بدون موضوع)",
"action_delete": "حذف",
"copy_as_file": "نسخ كملف",
"copied": "تم نسخ الملف. يمكنك لصقه في مجلد.",
"copy_failed": "تعذّر نسخ الملف.",
"reply_lost": "تعذّر الاحتفاظ بالرد الذي كتبته. افتح الرسالة وأجب من هناك."
},
"desktop_sign_in": {
"browser_button": "سجّل الدخول باستخدام المتصفح",
"browser_waiting": "أكمل تسجيل الدخول في المتصفح",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Aquest missatge s’ha mogut a {folder}.",
"window_not_ready": "La finestra nova no s’ha obert a temps. L’esborrany es queda aquí."
},
"desktopOs": {
"section_title": "Aplicació d'escriptori",
"section_description": "Com funciona l'aplicació amb l'ordinador.",
"close_to_tray_label": "Continua a la safata en tancar",
"close_to_tray_description": "El correu nou i els recordatoris continuen arribant després de tancar la finestra.",
"start_at_login_label": "Inicia en entrar a l'ordinador",
"start_at_login_description": "L'aplicació s'inicia sola quan entreu.",
"start_minimized_label": "Inicia a la safata",
"start_minimized_description": "No s'obre cap finestra en entrar. Obriu-la amb la icona de la safata.",
"attention_label": "Fes parpellejar la barra de tasques amb correu nou",
"attention_mac_label": "Fes botar la icona del Dock amb correu nou",
"attention_description": "Només quan l'aplicació no és al davant.",
"quiet_hours_label": "Hores de silenci",
"quiet_hours_description": "Cap notificació ni so en aquestes hores. El correu continua arribant.",
"quiet_hours_from": "De",
"quiet_hours_to": "a",
"show_shortcut_label": "Mostra l'aplicació amb una drecera de teclat",
"show_shortcut_description": "{keys} porta l'aplicació al davant des de qualsevol altra aplicació.",
"show_shortcut_taken": "Una altra aplicació fa servir {keys}. Tanqueu-la o desactiveu i torneu a activar això.",
"default_app_label": "Aplicació de correu predeterminada",
"default_app_description": "Obre els enllaços de correu i els fitxers .eml, .ics i .vcf amb aquesta aplicació.",
"default_app_is_default": "Aquesta aplicació obre els vostres enllaços de correu.",
"default_app_button": "Tria l'aplicació predeterminada…",
"new_mail": "Correu nou",
"no_subject": "(sense assumpte)",
"action_delete": "Suprimeix",
"copy_as_file": "Copia com a fitxer",
"copied": "Fitxer copiat. El podeu enganxar en una carpeta.",
"copy_failed": "No s'ha pogut copiar el fitxer.",
"reply_lost": "No s'ha pogut conservar la resposta que heu escrit. Obriu el missatge i responeu des d'allà."
},
"desktop_sign_in": {
"browser_button": "Inicia la sessió amb el navegador",
"browser_waiting": "Acaba d'iniciar la sessió al navegador",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Tato zpráva byla přesunuta do {folder}.",
"window_not_ready": "Nové okno se neotevřelo včas. Koncept zůstává zde."
},
"desktopOs": {
"section_title": "Desktopová aplikace",
"section_description": "Jak aplikace spolupracuje s vaším počítačem.",
"close_to_tray_label": "Po zavření běžet dál v oznamovací oblasti",
"close_to_tray_description": "Nová pošta a připomenutí chodí i po zavření okna.",
"start_at_login_label": "Spustit po přihlášení k počítači",
"start_at_login_description": "Aplikace se po přihlášení spustí sama.",
"start_minimized_label": "Spustit v oznamovací oblasti",
"start_minimized_description": "Po přihlášení se neotevře žádné okno. Otevřete ji ikonou v oznamovací oblasti.",
"attention_label": "Při nové poště blikat na hlavním panelu",
"attention_mac_label": "Při nové poště poskakovat ikonou v Docku",
"attention_description": "Jen když aplikace není v popředí.",
"quiet_hours_label": "Doba klidu",
"quiet_hours_description": "V tuto dobu žádná oznámení ani zvuky. Pošta dál přichází.",
"quiet_hours_from": "Od",
"quiet_hours_to": "do",
"show_shortcut_label": "Zobrazit aplikaci klávesovou zkratkou",
"show_shortcut_description": "{keys} přenese aplikaci do popředí z jakékoli jiné aplikace.",
"show_shortcut_taken": "{keys} používá jiná aplikace. Zavřete ji, nebo tuto volbu vypněte a znovu zapněte.",
"default_app_label": "Výchozí poštovní aplikace",
"default_app_description": "Otevírat poštovní odkazy a soubory .eml, .ics a .vcf v této aplikaci.",
"default_app_is_default": "Tato aplikace otevírá vaše poštovní odkazy.",
"default_app_button": "Vybrat výchozí aplikaci…",
"new_mail": "Nová pošta",
"no_subject": "(bez předmětu)",
"action_delete": "Smazat",
"copy_as_file": "Kopírovat jako soubor",
"copied": "Soubor zkopírován. Můžete ho vložit do složky.",
"copy_failed": "Soubor se nepodařilo zkopírovat.",
"reply_lost": "Napsanou odpověď se nepodařilo zachovat. Otevřete zprávu a odpovězte v ní."
},
"desktop_sign_in": {
"browser_button": "Přihlásit se v prohlížeči",
"browser_waiting": "Dokončete přihlášení v prohlížeči",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Denne besked er flyttet til {folder}.",
"window_not_ready": "Det nye vindue åbnede ikke i tide. Din kladde bliver her."
},
"desktopOs": {
"section_title": "Skrivebordsapp",
"section_description": "Sådan arbejder appen sammen med din computer.",
"close_to_tray_label": "Fortsæt i systembakken ved lukning",
"close_to_tray_description": "Ny post og påmindelser bliver ved med at komme, når du lukker vinduet.",
"start_at_login_label": "Start, når du logger på computeren",
"start_at_login_description": "Appen starter selv, når du logger på.",
"start_minimized_label": "Start i systembakken",
"start_minimized_description": "Der åbnes intet vindue ved logon. Åbn appen med ikonet i systembakken.",
"attention_label": "Blink i proceslinjen ved ny post",
"attention_mac_label": "Lad Dock-ikonet hoppe ved ny post",
"attention_description": "Kun når appen ikke er forrest.",
"quiet_hours_label": "Stille timer",
"quiet_hours_description": "Ingen notifikationer eller lyde i dette tidsrum. Post kommer stadig.",
"quiet_hours_from": "Fra",
"quiet_hours_to": "til",
"show_shortcut_label": "Vis appen med en tastaturgenvej",
"show_shortcut_description": "{keys} henter appen frem fra enhver anden app.",
"show_shortcut_taken": "En anden app bruger {keys}. Luk den app, eller slå dette fra og til igen.",
"default_app_label": "Standardmailapp",
"default_app_description": "Åbn maillinks og .eml-, .ics- og .vcf-filer med denne app.",
"default_app_is_default": "Denne app åbner dine maillinks.",
"default_app_button": "Vælg standardapp…",
"new_mail": "Ny post",
"no_subject": "(intet emne)",
"action_delete": "Slet",
"copy_as_file": "Kopiér som fil",
"copied": "Filen er kopieret. Du kan indsætte den i en mappe.",
"copy_failed": "Filen kunne ikke kopieres.",
"reply_lost": "Dit skrevne svar kunne ikke gemmes. Åbn beskeden, og svar der."
},
"desktop_sign_in": {
"browser_button": "Log ind med din browser",
"browser_waiting": "Gør login færdigt i din browser",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Diese Nachricht wurde nach {folder} verschoben.",
"window_not_ready": "Das neue Fenster wurde nicht rechtzeitig geöffnet. Ihr Entwurf bleibt hier."
},
"desktopOs": {
"section_title": "Desktop-App",
"section_description": "Wie die App mit Ihrem Computer zusammenarbeitet.",
"close_to_tray_label": "Beim Schließen im Infobereich weiterlaufen",
"close_to_tray_description": "Neue E-Mails und Erinnerungen kommen auch dann an, wenn Sie das Fenster schließen.",
"start_at_login_label": "Beim Anmelden am Computer starten",
"start_at_login_description": "Die App startet von selbst, wenn Sie sich anmelden.",
"start_minimized_label": "Im Infobereich starten",
"start_minimized_description": "Beim Anmelden öffnet sich kein Fenster. Öffnen Sie die App über das Symbol im Infobereich.",
"attention_label": "Taskleiste bei neuen E-Mails blinken lassen",
"attention_mac_label": "Dock-Symbol bei neuen E-Mails hüpfen lassen",
"attention_description": "Nur wenn die App nicht im Vordergrund ist.",
"quiet_hours_label": "Ruhezeiten",
"quiet_hours_description": "In dieser Zeit keine Benachrichtigungen und Töne. E-Mails kommen trotzdem an.",
"quiet_hours_from": "Von",
"quiet_hours_to": "bis",
"show_shortcut_label": "App mit einer Tastenkombination anzeigen",
"show_shortcut_description": "{keys} holt die App aus jeder anderen App nach vorne.",
"show_shortcut_taken": "Eine andere App verwendet {keys}. Schließen Sie diese App oder schalten Sie das hier aus und wieder ein.",
"default_app_label": "Standard-E-Mail-App",
"default_app_description": "Mail-Links und .eml-, .ics- und .vcf-Dateien mit dieser App öffnen.",
"default_app_is_default": "Diese App öffnet Ihre Mail-Links.",
"default_app_button": "Standard-App auswählen…",
"new_mail": "Neue E-Mail",
"no_subject": "(kein Betreff)",
"action_delete": "Löschen",
"copy_as_file": "Als Datei kopieren",
"copied": "Datei kopiert. Sie können sie in einen Ordner einfügen.",
"copy_failed": "Die Datei konnte nicht kopiert werden.",
"reply_lost": "Ihre eingegebene Antwort konnte nicht übernommen werden. Öffnen Sie die Nachricht und antworten Sie dort."
},
"desktop_sign_in": {
"browser_button": "Mit dem Browser anmelden",
"browser_waiting": "Schließen Sie die Anmeldung im Browser ab",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "This message was moved to {folder}.",
"window_not_ready": "The new window did not open in time. Your draft stays here."
},
"desktopOs": {
"section_title": "Desktop app",
"section_description": "How the app works with your computer.",
"close_to_tray_label": "Keep running in the tray when closed",
"close_to_tray_description": "New mail and reminders keep coming after you close the window.",
"start_at_login_label": "Start when you sign in to your computer",
"start_at_login_description": "The app starts by itself when you sign in.",
"start_minimized_label": "Start in the tray",
"start_minimized_description": "No window opens at sign-in. Use the tray icon to open it.",
"attention_label": "Flash the taskbar on new mail",
"attention_mac_label": "Bounce the Dock icon on new mail",
"attention_description": "Only when the app is not in front.",
"quiet_hours_label": "Quiet hours",
"quiet_hours_description": "No notifications or sounds at these times. Mail still arrives.",
"quiet_hours_from": "From",
"quiet_hours_to": "to",
"show_shortcut_label": "Show the app with a keyboard shortcut",
"show_shortcut_description": "{keys} brings the app to the front, from any other app.",
"show_shortcut_taken": "Another app is using {keys}. Close that app, or turn this off and on again.",
"default_app_label": "Default mail app",
"default_app_description": "Open mail links and .eml, .ics and .vcf files with this app.",
"default_app_is_default": "This app opens your mail links.",
"default_app_button": "Choose default app…",
"new_mail": "New mail",
"no_subject": "(no subject)",
"action_delete": "Delete",
"copy_as_file": "Copy as file",
"copied": "File copied. You can paste it into a folder.",
"copy_failed": "The file could not be copied.",
"reply_lost": "Your typed reply could not be kept. Open the message and reply there."
},
"desktop_sign_in": {
"browser_button": "Sign in with your browser",
"browser_waiting": "Finish signing in in your browser",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Este mensaje se movió a {folder}.",
"window_not_ready": "La ventana nueva no se abrió a tiempo. Tu borrador se queda aquí."
},
"desktopOs": {
"section_title": "Aplicación de escritorio",
"section_description": "Cómo funciona la aplicación con tu ordenador.",
"close_to_tray_label": "Seguir en la bandeja al cerrar",
"close_to_tray_description": "El correo nuevo y los recordatorios siguen llegando después de cerrar la ventana.",
"start_at_login_label": "Iniciar al entrar en el ordenador",
"start_at_login_description": "La aplicación se inicia sola cuando entras.",
"start_minimized_label": "Iniciar en la bandeja",
"start_minimized_description": "No se abre ninguna ventana al entrar. Ábrela con el icono de la bandeja.",
"attention_label": "Hacer parpadear la barra de tareas con correo nuevo",
"attention_mac_label": "Hacer rebotar el icono del Dock con correo nuevo",
"attention_description": "Solo cuando la aplicación no está delante.",
"quiet_hours_label": "Horas de silencio",
"quiet_hours_description": "Sin notificaciones ni sonidos en estas horas. El correo sigue llegando.",
"quiet_hours_from": "De",
"quiet_hours_to": "a",
"show_shortcut_label": "Mostrar la aplicación con un atajo de teclado",
"show_shortcut_description": "{keys} trae la aplicación al frente desde cualquier otra.",
"show_shortcut_taken": "Otra aplicación está usando {keys}. Ciérrala, o desactiva y vuelve a activar esta opción.",
"default_app_label": "Aplicación de correo predeterminada",
"default_app_description": "Abrir los enlaces de correo y los archivos .eml, .ics y .vcf con esta aplicación.",
"default_app_is_default": "Esta aplicación abre tus enlaces de correo.",
"default_app_button": "Elegir aplicación predeterminada…",
"new_mail": "Correo nuevo",
"no_subject": "(sin asunto)",
"action_delete": "Eliminar",
"copy_as_file": "Copiar como archivo",
"copied": "Archivo copiado. Puedes pegarlo en una carpeta.",
"copy_failed": "No se pudo copiar el archivo.",
"reply_lost": "No se pudo conservar la respuesta que escribiste. Abre el mensaje y responde desde allí."
},
"desktop_sign_in": {
"browser_button": "Iniciar sesión con el navegador",
"browser_waiting": "Termina de iniciar sesión en el navegador",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "See kiri teisaldati kausta {folder}.",
"window_not_ready": "Uus aken ei avanenud õigeks ajaks. Mustand jääb siia."
},
"desktopOs": {
"section_title": "Töölauarakendus",
"section_description": "Kuidas rakendus sinu arvutiga koos töötab.",
"close_to_tray_label": "Sulgemisel jätka salves",
"close_to_tray_description": "Uus kiri ja meeldetuletused saabuvad ka pärast akna sulgemist.",
"start_at_login_label": "Käivita arvutisse sisselogimisel",
"start_at_login_description": "Rakendus käivitub sisselogimisel ise.",
"start_minimized_label": "Käivita salves",
"start_minimized_description": "Sisselogimisel akent ei avata. Ava rakendus salve ikoonist.",
"attention_label": "Vilguta tegumiriba uue kirja korral",
"attention_mac_label": "Põrgata Docki ikooni uue kirja korral",
"attention_description": "Ainult siis, kui rakendus pole esiplaanil.",
"quiet_hours_label": "Vaikne aeg",
"quiet_hours_description": "Sel ajal teavitusi ega helisid pole. Kirjad saabuvad ikka.",
"quiet_hours_from": "Alates",
"quiet_hours_to": "kuni",
"show_shortcut_label": "Näita rakendust kiirklahviga",
"show_shortcut_description": "{keys} toob rakenduse esiplaanile igast teisest rakendusest.",
"show_shortcut_taken": "{keys} on teise rakenduse kasutuses. Sulge see rakendus või lülita see valik välja ja uuesti sisse.",
"default_app_label": "Vaikimisi e-posti rakendus",
"default_app_description": "Ava e-posti lingid ning .eml-, .ics- ja .vcf-failid selle rakendusega.",
"default_app_is_default": "See rakendus avab sinu e-posti lingid.",
"default_app_button": "Vali vaikerakendus…",
"new_mail": "Uus kiri",
"no_subject": "(teema puudub)",
"action_delete": "Kustuta",
"copy_as_file": "Kopeeri failina",
"copied": "Fail on kopeeritud. Saad selle kausta kleepida.",
"copy_failed": "Faili ei saanud kopeerida.",
"reply_lost": "Kirjutatud vastust ei saanud säilitada. Ava kiri ja vasta seal."
},
"desktop_sign_in": {
"browser_button": "Logi sisse brauseriga",
"browser_waiting": "Lõpeta sisselogimine brauseris",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "این پیام به {folder} منتقل شد.",
"window_not_ready": "پنجرهٔ جدید به‌موقع باز نشد. پیش‌نویس شما همین‌جا می‌ماند."
},
"desktopOs": {
"section_title": "برنامه دسکتاپ",
"section_description": "نحوه کار برنامه با رایانه شما.",
"close_to_tray_label": "هنگام بستن در سینی سیستم اجرا بماند",
"close_to_tray_description": "نامه‌های جدید و یادآوری‌ها پس از بستن پنجره هم می‌رسند.",
"start_at_login_label": "هنگام ورود به رایانه شروع شود",
"start_at_login_description": "برنامه هنگام ورود خودش شروع می‌شود.",
"start_minimized_label": "شروع در سینی سیستم",
"start_minimized_description": "هنگام ورود پنجره‌ای باز نمی‌شود. با نماد سینی سیستم آن را باز کنید.",
"attention_label": "چشمک نوار وظیفه هنگام نامه جدید",
"attention_mac_label": "جهش نماد Dock هنگام نامه جدید",
"attention_description": "فقط وقتی برنامه در جلو نیست.",
"quiet_hours_label": "ساعات سکوت",
"quiet_hours_description": "در این ساعات اعلان و صدایی نیست. نامه‌ها همچنان می‌رسند.",
"quiet_hours_from": "از",
"quiet_hours_to": "تا",
"show_shortcut_label": "نمایش برنامه با میان‌بر صفحه‌کلید",
"show_shortcut_description": "{keys} برنامه را از هر برنامه دیگری به جلو می‌آورد.",
"show_shortcut_taken": "برنامه دیگری از {keys} استفاده می‌کند. آن را ببندید، یا این گزینه را خاموش و دوباره روشن کنید.",
"default_app_label": "برنامه پیش‌فرض ایمیل",
"default_app_description": "باز کردن پیوندهای ایمیل و پرونده‌های ‎.eml، ‎.ics و ‎.vcf با این برنامه.",
"default_app_is_default": "این برنامه پیوندهای ایمیل شما را باز می‌کند.",
"default_app_button": "انتخاب برنامه پیش‌فرض…",
"new_mail": "نامه جدید",
"no_subject": "(بدون موضوع)",
"action_delete": "حذف",
"copy_as_file": "رونوشت به‌صورت پرونده",
"copied": "پرونده رونوشت شد. می‌توانید آن را در یک پوشه بچسبانید.",
"copy_failed": "رونوشت پرونده ممکن نشد.",
"reply_lost": "پاسخی که نوشتید نگه داشته نشد. پیام را باز کنید و از آنجا پاسخ دهید."
},
"desktop_sign_in": {
"browser_button": "ورود با مرورگر",
"browser_waiting": "ورود را در مرورگر کامل کنید",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Ce message a été déplacé vers {folder}.",
"window_not_ready": "La nouvelle fenêtre ne s’est pas ouverte à temps. Votre brouillon reste ici."
},
"desktopOs": {
"section_title": "Application de bureau",
"section_description": "Comment l'application fonctionne avec votre ordinateur.",
"close_to_tray_label": "Rester dans la zone de notification à la fermeture",
"close_to_tray_description": "Le courrier et les rappels continuent d'arriver après la fermeture de la fenêtre.",
"start_at_login_label": "Démarrer à l'ouverture de session",
"start_at_login_description": "L'application démarre seule quand vous ouvrez votre session.",
"start_minimized_label": "Démarrer dans la zone de notification",
"start_minimized_description": "Aucune fenêtre ne s'ouvre à l'ouverture de session. Ouvrez-la avec l'icône de la zone de notification.",
"attention_label": "Faire clignoter la barre des tâches à l'arrivée de courrier",
"attention_mac_label": "Faire rebondir l'icône du Dock à l'arrivée de courrier",
"attention_description": "Seulement quand l'application n'est pas au premier plan.",
"quiet_hours_label": "Heures calmes",
"quiet_hours_description": "Ni notifications ni sons pendant ces heures. Le courrier continue d'arriver.",
"quiet_hours_from": "De",
"quiet_hours_to": "à",
"show_shortcut_label": "Afficher l'application avec un raccourci clavier",
"show_shortcut_description": "{keys} amène l'application au premier plan depuis n'importe quelle autre.",
"show_shortcut_taken": "Une autre application utilise {keys}. Fermez-la, ou désactivez puis réactivez cette option.",
"default_app_label": "Application de messagerie par défaut",
"default_app_description": "Ouvrir les liens de messagerie et les fichiers .eml, .ics et .vcf avec cette application.",
"default_app_is_default": "Cette application ouvre vos liens de messagerie.",
"default_app_button": "Choisir l'application par défaut…",
"new_mail": "Nouveau courrier",
"no_subject": "(sans objet)",
"action_delete": "Supprimer",
"copy_as_file": "Copier en tant que fichier",
"copied": "Fichier copié. Vous pouvez le coller dans un dossier.",
"copy_failed": "Le fichier n'a pas pu être copié.",
"reply_lost": "Votre réponse saisie n'a pas pu être conservée. Ouvrez le message et répondez depuis celui-ci."
},
"desktop_sign_in": {
"browser_button": "Se connecter avec le navigateur",
"browser_waiting": "Terminez la connexion dans votre navigateur",
+31
View File
@@ -6546,6 +6546,37 @@
"message_moved_to": "ההודעה הזו הועברה אל {folder}.",
"window_not_ready": "החלון החדש לא נפתח בזמן. הטיוטה נשארת כאן."
},
"desktopOs": {
"section_title": "יישום שולחן עבודה",
"section_description": "איך היישום עובד עם המחשב שלך.",
"close_to_tray_label": "להמשיך לפעול במגש המערכת בסגירה",
"close_to_tray_description": "דואר חדש ותזכורות ממשיכים להגיע אחרי סגירת החלון.",
"start_at_login_label": "להפעיל בכניסה למחשב",
"start_at_login_description": "היישום מופעל מעצמו כשנכנסים.",
"start_minimized_label": "להפעיל במגש המערכת",
"start_minimized_description": "בכניסה לא נפתח חלון. פותחים אותו מהסמל במגש המערכת.",
"attention_label": "הבהוב בשורת המשימות בדואר חדש",
"attention_mac_label": "קפיצת סמל ה-Dock בדואר חדש",
"attention_description": "רק כשהיישום לא בחזית.",
"quiet_hours_label": "שעות שקט",
"quiet_hours_description": "בשעות אלה אין התראות וצלילים. הדואר ממשיך להגיע.",
"quiet_hours_from": "מ-",
"quiet_hours_to": "עד",
"show_shortcut_label": "הצגת היישום בקיצור מקשים",
"show_shortcut_description": "{keys} מביא את היישום לחזית מכל יישום אחר.",
"show_shortcut_taken": "יישום אחר משתמש ב-{keys}. סגרו אותו, או כבו והפעילו מחדש את האפשרות הזו.",
"default_app_label": "יישום הדואר המוגדר כברירת מחדל",
"default_app_description": "פתיחת קישורי דואר וקובצי ‎.eml,‏ ‎.ics ו-‎.vcf ביישום זה.",
"default_app_is_default": "יישום זה פותח את קישורי הדואר שלך.",
"default_app_button": "בחירת יישום ברירת מחדל…",
"new_mail": "דואר חדש",
"no_subject": "(ללא נושא)",
"action_delete": "מחיקה",
"copy_as_file": "העתקה כקובץ",
"copied": "הקובץ הועתק. אפשר להדביק אותו בתיקייה.",
"copy_failed": "לא ניתן להעתיק את הקובץ.",
"reply_lost": "לא ניתן היה לשמור את התשובה שהקלדת. פתחו את ההודעה והשיבו משם."
},
"desktop_sign_in": {
"browser_button": "כניסה באמצעות הדפדפן",
"browser_waiting": "יש להשלים את הכניסה בדפדפן",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Ezt az üzenetet ide helyezték át: {folder}.",
"window_not_ready": "Az új ablak nem nyílt meg időben. A piszkozat itt marad."
},
"desktopOs": {
"section_title": "Asztali alkalmazás",
"section_description": "Hogyan működik az alkalmazás a számítógépével.",
"close_to_tray_label": "Bezáráskor fusson tovább a tálcán",
"close_to_tray_description": "Az új levelek és emlékeztetők az ablak bezárása után is megérkeznek.",
"start_at_login_label": "Indítás a számítógépre való bejelentkezéskor",
"start_at_login_description": "Az alkalmazás bejelentkezéskor magától elindul.",
"start_minimized_label": "Indítás a tálcán",
"start_minimized_description": "Bejelentkezéskor nem nyílik ablak. A tálcaikonnal nyithatja meg.",
"attention_label": "Tálca villogtatása új levélnél",
"attention_mac_label": "Dock-ikon ugráltatása új levélnél",
"attention_description": "Csak ha az alkalmazás nincs előtérben.",
"quiet_hours_label": "Csendes órák",
"quiet_hours_description": "Ekkor nincs értesítés és hang. A levelek továbbra is megérkeznek.",
"quiet_hours_from": "Ettől",
"quiet_hours_to": "eddig",
"show_shortcut_label": "Az alkalmazás megjelenítése billentyűparanccsal",
"show_shortcut_description": "{keys} bármely más alkalmazásból előtérbe hozza az alkalmazást.",
"show_shortcut_taken": "Egy másik alkalmazás használja ezt: {keys}. Zárja be azt, vagy kapcsolja ki és be ezt a beállítást.",
"default_app_label": "Alapértelmezett levelezőalkalmazás",
"default_app_description": "A levélhivatkozások és a .eml, .ics és .vcf fájlok megnyitása ezzel az alkalmazással.",
"default_app_is_default": "Ez az alkalmazás nyitja meg a levélhivatkozásait.",
"default_app_button": "Alapértelmezett alkalmazás kiválasztása…",
"new_mail": "Új levél",
"no_subject": "(nincs tárgy)",
"action_delete": "Törlés",
"copy_as_file": "Másolás fájlként",
"copied": "Fájl másolva. Beillesztheti egy mappába.",
"copy_failed": "A fájlt nem sikerült másolni.",
"reply_lost": "A beírt válasz nem maradt meg. Nyissa meg az üzenetet, és válaszoljon ott."
},
"desktop_sign_in": {
"browser_button": "Bejelentkezés böngészővel",
"browser_waiting": "Fejezze be a bejelentkezést a böngészőben",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Questo messaggio è stato spostato in {folder}.",
"window_not_ready": "La nuova finestra non si è aperta in tempo. La bozza rimane qui."
},
"desktopOs": {
"section_title": "App desktop",
"section_description": "Come l'app funziona con il tuo computer.",
"close_to_tray_label": "Resta nell'area di notifica alla chiusura",
"close_to_tray_description": "La nuova posta e i promemoria continuano ad arrivare dopo la chiusura della finestra.",
"start_at_login_label": "Avvia all'accesso al computer",
"start_at_login_description": "L'app si avvia da sola quando accedi.",
"start_minimized_label": "Avvia nell'area di notifica",
"start_minimized_description": "All'accesso non si apre alcuna finestra. Aprila con l'icona nell'area di notifica.",
"attention_label": "Fai lampeggiare la barra delle applicazioni con nuova posta",
"attention_mac_label": "Fai rimbalzare l'icona del Dock con nuova posta",
"attention_description": "Solo quando l'app non è in primo piano.",
"quiet_hours_label": "Ore di silenzio",
"quiet_hours_description": "Nessuna notifica o suono in queste ore. La posta continua ad arrivare.",
"quiet_hours_from": "Da",
"quiet_hours_to": "a",
"show_shortcut_label": "Mostra l'app con una scorciatoia da tastiera",
"show_shortcut_description": "{keys} porta l'app in primo piano da qualsiasi altra app.",
"show_shortcut_taken": "Un'altra app usa {keys}. Chiudila, oppure disattiva e riattiva questa opzione.",
"default_app_label": "App di posta predefinita",
"default_app_description": "Apri i link di posta e i file .eml, .ics e .vcf con questa app.",
"default_app_is_default": "Questa app apre i tuoi link di posta.",
"default_app_button": "Scegli app predefinita…",
"new_mail": "Nuova posta",
"no_subject": "(nessun oggetto)",
"action_delete": "Elimina",
"copy_as_file": "Copia come file",
"copied": "File copiato. Puoi incollarlo in una cartella.",
"copy_failed": "Impossibile copiare il file.",
"reply_lost": "Non è stato possibile conservare la risposta digitata. Apri il messaggio e rispondi da lì."
},
"desktop_sign_in": {
"browser_button": "Accedi con il browser",
"browser_waiting": "Completa l'accesso nel browser",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "このメッセージは {folder} に移動されました。",
"window_not_ready": "新しいウィンドウが時間内に開きませんでした。下書きはここに残ります。"
},
"desktopOs": {
"section_title": "デスクトップアプリ",
"section_description": "アプリとパソコンの連携方法です。",
"close_to_tray_label": "閉じてもトレイで実行を続ける",
"close_to_tray_description": "ウィンドウを閉じても新着メールとリマインダーは届きます。",
"start_at_login_label": "パソコンへのサインイン時に起動",
"start_at_login_description": "サインインするとアプリが自動で起動します。",
"start_minimized_label": "トレイで起動",
"start_minimized_description": "サインイン時にウィンドウは開きません。トレイのアイコンから開いてください。",
"attention_label": "新着メールでタスクバーを点滅",
"attention_mac_label": "新着メールで Dock アイコンを跳ねさせる",
"attention_description": "アプリが前面にないときのみ。",
"quiet_hours_label": "おやすみ時間",
"quiet_hours_description": "この時間は通知も音も出ません。メールは届きます。",
"quiet_hours_from": "開始",
"quiet_hours_to": "終了",
"show_shortcut_label": "キーボードショートカットでアプリを表示",
"show_shortcut_description": "{keys} で、ほかのアプリからこのアプリを前面に表示します。",
"show_shortcut_taken": "別のアプリが {keys} を使っています。そのアプリを閉じるか、この設定をオフにしてからオンに戻してください。",
"default_app_label": "既定のメールアプリ",
"default_app_description": "メールのリンクと .eml、.ics、.vcf ファイルをこのアプリで開きます。",
"default_app_is_default": "メールのリンクはこのアプリで開きます。",
"default_app_button": "既定のアプリを選択…",
"new_mail": "新着メール",
"no_subject": "(件名なし)",
"action_delete": "削除",
"copy_as_file": "ファイルとしてコピー",
"copied": "ファイルをコピーしました。フォルダーに貼り付けられます。",
"copy_failed": "ファイルをコピーできませんでした。",
"reply_lost": "入力した返信を保持できませんでした。メッセージを開いて返信してください。"
},
"desktop_sign_in": {
"browser_button": "ブラウザでサインイン",
"browser_waiting": "ブラウザでサインインを完了してください",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "이 메시지는 {folder}(으)로 이동되었습니다.",
"window_not_ready": "새 창이 제때 열리지 않았습니다. 임시 보관 메일은 여기에 남아 있습니다."
},
"desktopOs": {
"section_title": "데스크톱 앱",
"section_description": "앱이 컴퓨터와 함께 작동하는 방식입니다.",
"close_to_tray_label": "닫을 때 트레이에서 계속 실행",
"close_to_tray_description": "창을 닫아도 새 메일과 리마인더가 계속 도착합니다.",
"start_at_login_label": "컴퓨터에 로그인할 때 시작",
"start_at_login_description": "로그인하면 앱이 자동으로 시작됩니다.",
"start_minimized_label": "트레이에서 시작",
"start_minimized_description": "로그인할 때 창이 열리지 않습니다. 트레이 아이콘으로 여세요.",
"attention_label": "새 메일이 오면 작업 표시줄 깜박이기",
"attention_mac_label": "새 메일이 오면 Dock 아이콘 튀기기",
"attention_description": "앱이 앞에 있지 않을 때만 작동합니다.",
"quiet_hours_label": "방해 금지 시간",
"quiet_hours_description": "이 시간에는 알림과 소리가 없습니다. 메일은 계속 도착합니다.",
"quiet_hours_from": "시작",
"quiet_hours_to": "종료",
"show_shortcut_label": "키보드 단축키로 앱 표시",
"show_shortcut_description": "{keys} 키로 다른 앱에서도 이 앱을 앞으로 가져옵니다.",
"show_shortcut_taken": "다른 앱이 {keys} 키를 사용 중입니다. 그 앱을 닫거나 이 옵션을 껐다가 다시 켜세요.",
"default_app_label": "기본 메일 앱",
"default_app_description": "메일 링크와 .eml, .ics, .vcf 파일을 이 앱으로 엽니다.",
"default_app_is_default": "이 앱이 메일 링크를 엽니다.",
"default_app_button": "기본 앱 선택…",
"new_mail": "새 메일",
"no_subject": "(제목 없음)",
"action_delete": "삭제",
"copy_as_file": "파일로 복사",
"copied": "파일을 복사했습니다. 폴더에 붙여넣을 수 있습니다.",
"copy_failed": "파일을 복사할 수 없습니다.",
"reply_lost": "입력한 답장을 유지할 수 없습니다. 메시지를 열어 답장하세요."
},
"desktop_sign_in": {
"browser_button": "브라우저로 로그인",
"browser_waiting": "브라우저에서 로그인을 마치세요",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Šis ziņojums tika pārvietots uz {folder}.",
"window_not_ready": "Jaunais logs neatvērās laikus. Melnraksts paliek šeit."
},
"desktopOs": {
"section_title": "Darbvirsmas lietotne",
"section_description": "Kā lietotne darbojas ar jūsu datoru.",
"close_to_tray_label": "Aizverot turpināt darbu teknē",
"close_to_tray_description": "Jauns pasts un atgādinājumi turpina pienākt arī pēc loga aizvēršanas.",
"start_at_login_label": "Sākt, piesakoties datorā",
"start_at_login_description": "Lietotne sākas pati, kad piesakāties.",
"start_minimized_label": "Sākt teknē",
"start_minimized_description": "Piesakoties logs neatveras. Atveriet to ar teknes ikonu.",
"attention_label": "Mirgot uzdevumjoslā, pienākot pastam",
"attention_mac_label": "Lēkāt Dock ikonai, pienākot pastam",
"attention_description": "Tikai tad, ja lietotne nav priekšplānā.",
"quiet_hours_label": "Klusuma stundas",
"quiet_hours_description": "Šajā laikā nav paziņojumu un skaņu. Pasts turpina pienākt.",
"quiet_hours_from": "No",
"quiet_hours_to": "līdz",
"show_shortcut_label": "Rādīt lietotni ar īsinājumtaustiņu",
"show_shortcut_description": "{keys} parāda lietotni priekšplānā no jebkuras citas lietotnes.",
"show_shortcut_taken": "{keys} izmanto cita lietotne. Aizveriet to vai izslēdziet un atkal ieslēdziet šo iestatījumu.",
"default_app_label": "Noklusējuma pasta lietotne",
"default_app_description": "Atvērt pasta saites un .eml, .ics un .vcf failus ar šo lietotni.",
"default_app_is_default": "Šī lietotne atver jūsu pasta saites.",
"default_app_button": "Izvēlēties noklusējuma lietotni…",
"new_mail": "Jauns pasts",
"no_subject": "(nav tēmas)",
"action_delete": "Dzēst",
"copy_as_file": "Kopēt kā failu",
"copied": "Fails nokopēts. Varat to ielīmēt mapē.",
"copy_failed": "Failu neizdevās nokopēt.",
"reply_lost": "Ievadīto atbildi neizdevās saglabāt. Atveriet ziņojumu un atbildiet tur."
},
"desktop_sign_in": {
"browser_button": "Pierakstīties ar pārlūku",
"browser_waiting": "Pabeidziet pierakstīšanos pārlūkā",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Энэ мессежийг {folder} руу зөөсөн.",
"window_not_ready": "Шинэ цонх цагтаа нээгдсэнгүй. Таны ноорог энд үлдэнэ."
},
"desktopOs": {
"section_title": "Ширээний апп",
"section_description": "Апп таны компьютертэй хэрхэн ажиллах.",
"close_to_tray_label": "Хаахад тавиур дээр үргэлжлүүлэн ажиллах",
"close_to_tray_description": "Цонхыг хаасны дараа ч шинэ захиа, сануулга ирсээр байна.",
"start_at_login_label": "Компьютерт нэвтрэхэд эхлүүлэх",
"start_at_login_description": "Нэвтрэхэд апп өөрөө эхэлнэ.",
"start_minimized_label": "Тавиур дээр эхлүүлэх",
"start_minimized_description": "Нэвтрэхэд цонх нээгдэхгүй. Тавиурын дүрсээр нээнэ үү.",
"attention_label": "Шинэ захиа ирэхэд ажлын самбарыг анивчуулах",
"attention_mac_label": "Шинэ захиа ирэхэд Dock дүрсийг үсрүүлэх",
"attention_description": "Зөвхөн апп урд талд байхгүй үед.",
"quiet_hours_label": "Чимээгүй цаг",
"quiet_hours_description": "Энэ үед мэдэгдэл, дуу гарахгүй. Захиа ирсээр байна.",
"quiet_hours_from": "Эхлэх",
"quiet_hours_to": "хүртэл",
"show_shortcut_label": "Гарын товчлолоор аппыг харуулах",
"show_shortcut_description": "{keys} нь аль ч аппаас энэ аппыг урд гаргана.",
"show_shortcut_taken": "{keys}-г өөр апп ашиглаж байна. Тэр аппыг хаах, эсвэл үүнийг унтраагаад дахин асаана уу.",
"default_app_label": "Үндсэн шуудангийн апп",
"default_app_description": "Шуудангийн холбоос болон .eml, .ics, .vcf файлыг энэ аппаар нээх.",
"default_app_is_default": "Энэ апп таны шуудангийн холбоосыг нээдэг.",
"default_app_button": "Үндсэн апп сонгох…",
"new_mail": "Шинэ захиа",
"no_subject": "(гарчиггүй)",
"action_delete": "Устгах",
"copy_as_file": "Файл болгож хуулах",
"copied": "Файлыг хууллаа. Хавтсанд буулгаж болно.",
"copy_failed": "Файлыг хуулж чадсангүй.",
"reply_lost": "Бичсэн хариуг хадгалж чадсангүй. Захиаг нээгээд тэндээс хариулна уу."
},
"desktop_sign_in": {
"browser_button": "Хөтчөөр нэвтрэх",
"browser_waiting": "Нэвтрэлтээ хөтөч дээр дуусгана уу",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Denne meldingen ble flyttet til {folder}.",
"window_not_ready": "Det nye vinduet åpnet seg ikke i tide. Utkastet blir her."
},
"desktopOs": {
"section_title": "Skrivebordsapp",
"section_description": "Slik samarbeider appen med datamaskinen din.",
"close_to_tray_label": "Fortsett i systemstatusfeltet ved lukking",
"close_to_tray_description": "Ny e-post og påminnelser fortsetter å komme etter at du lukker vinduet.",
"start_at_login_label": "Start når du logger på datamaskinen",
"start_at_login_description": "Appen starter av seg selv når du logger på.",
"start_minimized_label": "Start i systemstatusfeltet",
"start_minimized_description": "Det åpnes ikke noe vindu ved pålogging. Åpne appen med ikonet i systemstatusfeltet.",
"attention_label": "Blink i oppgavelinjen ved ny e-post",
"attention_mac_label": "La Dock-ikonet sprette ved ny e-post",
"attention_description": "Bare når appen ikke er foran.",
"quiet_hours_label": "Stille timer",
"quiet_hours_description": "Ingen varsler eller lyder i dette tidsrommet. E-post kommer fortsatt.",
"quiet_hours_from": "Fra",
"quiet_hours_to": "til",
"show_shortcut_label": "Vis appen med en hurtigtast",
"show_shortcut_description": "{keys} henter appen frem fra hvilken som helst annen app.",
"show_shortcut_taken": "En annen app bruker {keys}. Lukk den appen, eller slå dette av og på igjen.",
"default_app_label": "Standard e-postapp",
"default_app_description": "Åpne e-postlenker og .eml-, .ics- og .vcf-filer med denne appen.",
"default_app_is_default": "Denne appen åpner e-postlenkene dine.",
"default_app_button": "Velg standardapp…",
"new_mail": "Ny e-post",
"no_subject": "(uten emne)",
"action_delete": "Slett",
"copy_as_file": "Kopier som fil",
"copied": "Filen er kopiert. Du kan lime den inn i en mappe.",
"copy_failed": "Filen kunne ikke kopieres.",
"reply_lost": "Svaret du skrev, kunne ikke beholdes. Åpne meldingen og svar der."
},
"desktop_sign_in": {
"browser_button": "Logg på med nettleseren",
"browser_waiting": "Fullfør påloggingen i nettleseren",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Dit bericht is verplaatst naar {folder}.",
"window_not_ready": "Het nieuwe venster is niet op tijd geopend. Je concept blijft hier."
},
"desktopOs": {
"section_title": "Desktop-app",
"section_description": "Hoe de app samenwerkt met je computer.",
"close_to_tray_label": "Bij sluiten doorgaan in het systeemvak",
"close_to_tray_description": "Nieuwe mail en herinneringen blijven komen nadat je het venster sluit.",
"start_at_login_label": "Starten als je je aanmeldt op de computer",
"start_at_login_description": "De app start vanzelf als je je aanmeldt.",
"start_minimized_label": "Starten in het systeemvak",
"start_minimized_description": "Bij het aanmelden opent geen venster. Open de app met het pictogram in het systeemvak.",
"attention_label": "Taakbalk laten knipperen bij nieuwe mail",
"attention_mac_label": "Dock-pictogram laten stuiteren bij nieuwe mail",
"attention_description": "Alleen als de app niet vooraan staat.",
"quiet_hours_label": "Stille uren",
"quiet_hours_description": "Geen meldingen of geluiden op deze tijden. Mail blijft binnenkomen.",
"quiet_hours_from": "Van",
"quiet_hours_to": "tot",
"show_shortcut_label": "App tonen met een sneltoets",
"show_shortcut_description": "{keys} haalt de app naar voren vanuit elke andere app.",
"show_shortcut_taken": "Een andere app gebruikt {keys}. Sluit die app, of zet dit uit en weer aan.",
"default_app_label": "Standaard-mailapp",
"default_app_description": "Maillinks en .eml-, .ics- en .vcf-bestanden openen met deze app.",
"default_app_is_default": "Deze app opent je maillinks.",
"default_app_button": "Standaard-app kiezen…",
"new_mail": "Nieuwe mail",
"no_subject": "(geen onderwerp)",
"action_delete": "Verwijderen",
"copy_as_file": "Kopiëren als bestand",
"copied": "Bestand gekopieerd. Je kunt het in een map plakken.",
"copy_failed": "Het bestand kon niet worden gekopieerd.",
"reply_lost": "Je getypte antwoord kon niet worden bewaard. Open het bericht en antwoord daar."
},
"desktop_sign_in": {
"browser_button": "Inloggen met je browser",
"browser_waiting": "Rond het inloggen af in je browser",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Ta wiadomość została przeniesiona do {folder}.",
"window_not_ready": "Nowe okno nie otworzyło się na czas. Twój szkic zostaje tutaj."
},
"desktopOs": {
"section_title": "Aplikacja na komputer",
"section_description": "Jak aplikacja współpracuje z Twoim komputerem.",
"close_to_tray_label": "Po zamknięciu działaj dalej w zasobniku",
"close_to_tray_description": "Nowa poczta i przypomnienia docierają też po zamknięciu okna.",
"start_at_login_label": "Uruchamiaj po zalogowaniu do komputera",
"start_at_login_description": "Aplikacja uruchamia się sama po zalogowaniu.",
"start_minimized_label": "Uruchamiaj w zasobniku",
"start_minimized_description": "Po zalogowaniu nie otwiera się okno. Otwórz je ikoną w zasobniku.",
"attention_label": "Migaj na pasku zadań przy nowej poczcie",
"attention_mac_label": "Podskakuj ikoną w Docku przy nowej poczcie",
"attention_description": "Tylko gdy aplikacja nie jest na wierzchu.",
"quiet_hours_label": "Godziny ciszy",
"quiet_hours_description": "W tym czasie bez powiadomień i dźwięków. Poczta nadal dociera.",
"quiet_hours_from": "Od",
"quiet_hours_to": "do",
"show_shortcut_label": "Pokazuj aplikację skrótem klawiszowym",
"show_shortcut_description": "{keys} przywołuje aplikację z każdej innej aplikacji.",
"show_shortcut_taken": "Skrót {keys} jest używany przez inną aplikację. Zamknij ją albo wyłącz i włącz tę opcję.",
"default_app_label": "Domyślna aplikacja poczty",
"default_app_description": "Otwieraj linki pocztowe i pliki .eml, .ics i .vcf w tej aplikacji.",
"default_app_is_default": "Ta aplikacja otwiera Twoje linki pocztowe.",
"default_app_button": "Wybierz domyślną aplikację…",
"new_mail": "Nowa poczta",
"no_subject": "(bez tematu)",
"action_delete": "Usuń",
"copy_as_file": "Kopiuj jako plik",
"copied": "Plik skopiowany. Możesz wkleić go do folderu.",
"copy_failed": "Nie udało się skopiować pliku.",
"reply_lost": "Nie udało się zachować wpisanej odpowiedzi. Otwórz wiadomość i odpowiedz w niej."
},
"desktop_sign_in": {
"browser_button": "Zaloguj się w przeglądarce",
"browser_waiting": "Dokończ logowanie w przeglądarce",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Esta mensagem foi movida para {folder}.",
"window_not_ready": "A nova janela não abriu a tempo. O seu rascunho fica aqui."
},
"desktopOs": {
"section_title": "Aplicação de ambiente de trabalho",
"section_description": "Como a aplicação funciona com o seu computador.",
"close_to_tray_label": "Continuar na bandeja ao fechar",
"close_to_tray_description": "O correio novo e os lembretes continuam a chegar depois de fechar a janela.",
"start_at_login_label": "Iniciar ao entrar no computador",
"start_at_login_description": "A aplicação inicia sozinha quando entra.",
"start_minimized_label": "Iniciar na bandeja",
"start_minimized_description": "Não abre nenhuma janela ao entrar. Abra-a com o ícone da bandeja.",
"attention_label": "Piscar a barra de tarefas com correio novo",
"attention_mac_label": "Saltar o ícone da Dock com correio novo",
"attention_description": "Só quando a aplicação não está à frente.",
"quiet_hours_label": "Horas de silêncio",
"quiet_hours_description": "Sem notificações nem sons nestas horas. O correio continua a chegar.",
"quiet_hours_from": "De",
"quiet_hours_to": "até",
"show_shortcut_label": "Mostrar a aplicação com um atalho de teclado",
"show_shortcut_description": "{keys} traz a aplicação para a frente a partir de qualquer outra.",
"show_shortcut_taken": "Outra aplicação está a usar {keys}. Feche-a, ou desative e volte a ativar esta opção.",
"default_app_label": "Aplicação de correio predefinida",
"default_app_description": "Abrir ligações de correio e ficheiros .eml, .ics e .vcf com esta aplicação.",
"default_app_is_default": "Esta aplicação abre as suas ligações de correio.",
"default_app_button": "Escolher aplicação predefinida…",
"new_mail": "Correio novo",
"no_subject": "(sem assunto)",
"action_delete": "Eliminar",
"copy_as_file": "Copiar como ficheiro",
"copied": "Ficheiro copiado. Pode colá-lo numa pasta.",
"copy_failed": "Não foi possível copiar o ficheiro.",
"reply_lost": "Não foi possível guardar a resposta escrita. Abra a mensagem e responda a partir dela."
},
"desktop_sign_in": {
"browser_button": "Entrar com o navegador",
"browser_waiting": "Termine de entrar no navegador",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Acest mesaj a fost mutat în {folder}.",
"window_not_ready": "Fereastra nouă nu s-a deschis la timp. Ciorna rămâne aici."
},
"desktopOs": {
"section_title": "Aplicație desktop",
"section_description": "Cum funcționează aplicația cu computerul tău.",
"close_to_tray_label": "Rămâne în zona de notificări la închidere",
"close_to_tray_description": "E-mailurile noi și mementourile continuă să sosească după închiderea ferestrei.",
"start_at_login_label": "Pornește la conectarea în computer",
"start_at_login_description": "Aplicația pornește singură când te conectezi.",
"start_minimized_label": "Pornește în zona de notificări",
"start_minimized_description": "La conectare nu se deschide nicio fereastră. Deschide-o din pictograma din zona de notificări.",
"attention_label": "Clipește în bara de activități la e-mail nou",
"attention_mac_label": "Sare pictograma din Dock la e-mail nou",
"attention_description": "Doar când aplicația nu este în față.",
"quiet_hours_label": "Ore de liniște",
"quiet_hours_description": "Fără notificări sau sunete în aceste ore. E-mailurile sosesc în continuare.",
"quiet_hours_from": "De la",
"quiet_hours_to": "până la",
"show_shortcut_label": "Arată aplicația cu o comandă rapidă de la tastatură",
"show_shortcut_description": "{keys} aduce aplicația în față din orice altă aplicație.",
"show_shortcut_taken": "Altă aplicație folosește {keys}. Închide-o sau dezactivează și reactivează această opțiune.",
"default_app_label": "Aplicația de e-mail implicită",
"default_app_description": "Deschide linkurile de e-mail și fișierele .eml, .ics și .vcf cu această aplicație.",
"default_app_is_default": "Această aplicație îți deschide linkurile de e-mail.",
"default_app_button": "Alege aplicația implicită…",
"new_mail": "E-mail nou",
"no_subject": "(fără subiect)",
"action_delete": "Șterge",
"copy_as_file": "Copiază ca fișier",
"copied": "Fișier copiat. Îl poți lipi într-un dosar.",
"copy_failed": "Fișierul nu a putut fi copiat.",
"reply_lost": "Răspunsul scris nu a putut fi păstrat. Deschide mesajul și răspunde de acolo."
},
"desktop_sign_in": {
"browser_button": "Conectează-te cu browserul",
"browser_waiting": "Finalizează conectarea în browser",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Это письмо перемещено в {folder}.",
"window_not_ready": "Новое окно не открылось вовремя. Черновик остаётся здесь."
},
"desktopOs": {
"section_title": "Настольное приложение",
"section_description": "Как приложение работает с вашим компьютером.",
"close_to_tray_label": "При закрытии продолжать работу в трее",
"close_to_tray_description": "Новая почта и напоминания приходят и после закрытия окна.",
"start_at_login_label": "Запускать при входе в систему",
"start_at_login_description": "Приложение запускается само, когда вы входите.",
"start_minimized_label": "Запускать в трее",
"start_minimized_description": "При входе окно не открывается. Откройте его значком в трее.",
"attention_label": "Мигать на панели задач при новой почте",
"attention_mac_label": "Подпрыгивать значком в Dock при новой почте",
"attention_description": "Только когда приложение не на переднем плане.",
"quiet_hours_label": "Тихие часы",
"quiet_hours_description": "В это время нет уведомлений и звуков. Почта продолжает приходить.",
"quiet_hours_from": "С",
"quiet_hours_to": "до",
"show_shortcut_label": "Показывать приложение сочетанием клавиш",
"show_shortcut_description": "{keys} выводит приложение на передний план из любого другого.",
"show_shortcut_taken": "{keys} использует другое приложение. Закройте его или выключите и снова включите этот параметр.",
"default_app_label": "Почтовое приложение по умолчанию",
"default_app_description": "Открывать почтовые ссылки и файлы .eml, .ics и .vcf в этом приложении.",
"default_app_is_default": "Это приложение открывает ваши почтовые ссылки.",
"default_app_button": "Выбрать приложение по умолчанию…",
"new_mail": "Новая почта",
"no_subject": "(без темы)",
"action_delete": "Удалить",
"copy_as_file": "Копировать как файл",
"copied": "Файл скопирован. Его можно вставить в папку.",
"copy_failed": "Не удалось скопировать файл.",
"reply_lost": "Не удалось сохранить введённый ответ. Откройте письмо и ответьте из него."
},
"desktop_sign_in": {
"browser_button": "Войти через браузер",
"browser_waiting": "Завершите вход в браузере",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Táto správa bola presunutá do {folder}.",
"window_not_ready": "Nové okno sa neotvorilo včas. Koncept zostáva tu."
},
"desktopOs": {
"section_title": "Počítačová aplikácia",
"section_description": "Ako aplikácia spolupracuje s vaším počítačom.",
"close_to_tray_label": "Po zatvorení bežať ďalej v oblasti oznámení",
"close_to_tray_description": "Nová pošta a pripomienky prichádzajú aj po zatvorení okna.",
"start_at_login_label": "Spustiť po prihlásení do počítača",
"start_at_login_description": "Aplikácia sa po prihlásení spustí sama.",
"start_minimized_label": "Spustiť v oblasti oznámení",
"start_minimized_description": "Po prihlásení sa neotvorí žiadne okno. Otvorte ju ikonou v oblasti oznámení.",
"attention_label": "Pri novej pošte blikať na paneli úloh",
"attention_mac_label": "Pri novej pošte poskakovať ikonou v Docku",
"attention_description": "Len keď aplikácia nie je v popredí.",
"quiet_hours_label": "Čas pokoja",
"quiet_hours_description": "V tomto čase žiadne oznámenia ani zvuky. Pošta prichádza ďalej.",
"quiet_hours_from": "Od",
"quiet_hours_to": "do",
"show_shortcut_label": "Zobraziť aplikáciu klávesovou skratkou",
"show_shortcut_description": "{keys} prenesie aplikáciu do popredia z akejkoľvek inej aplikácie.",
"show_shortcut_taken": "{keys} používa iná aplikácia. Zatvorte ju alebo túto možnosť vypnite a znova zapnite.",
"default_app_label": "Predvolená poštová aplikácia",
"default_app_description": "Otvárať poštové odkazy a súbory .eml, .ics a .vcf v tejto aplikácii.",
"default_app_is_default": "Táto aplikácia otvára vaše poštové odkazy.",
"default_app_button": "Vybrať predvolenú aplikáciu…",
"new_mail": "Nová pošta",
"no_subject": "(bez predmetu)",
"action_delete": "Odstrániť",
"copy_as_file": "Kopírovať ako súbor",
"copied": "Súbor skopírovaný. Môžete ho vložiť do priečinka.",
"copy_failed": "Súbor sa nepodarilo skopírovať.",
"reply_lost": "Napísanú odpoveď sa nepodarilo zachovať. Otvorte správu a odpovedzte v nej."
},
"desktop_sign_in": {
"browser_button": "Prihlásiť sa v prehliadači",
"browser_waiting": "Dokončite prihlásenie v prehliadači",
+31
View File
@@ -6553,6 +6553,37 @@
"message_moved_to": "ข้อความนี้ถูกย้ายไปยัง {folder}",
"window_not_ready": "หน้าต่างใหม่เปิดไม่ทัน แบบร่างของคุณยังอยู่ที่นี่"
},
"desktopOs": {
"section_title": "แอปเดสก์ท็อป",
"section_description": "วิธีที่แอปทำงานกับคอมพิวเตอร์ของคุณ",
"close_to_tray_label": "ทำงานต่อในถาดระบบเมื่อปิด",
"close_to_tray_description": "เมลใหม่และการแจ้งเตือนยังเข้ามาหลังปิดหน้าต่าง",
"start_at_login_label": "เริ่มเมื่อลงชื่อเข้าใช้คอมพิวเตอร์",
"start_at_login_description": "แอปเริ่มเองเมื่อคุณลงชื่อเข้าใช้",
"start_minimized_label": "เริ่มในถาดระบบ",
"start_minimized_description": "ไม่มีหน้าต่างเปิดเมื่อลงชื่อเข้าใช้ เปิดด้วยไอคอนในถาดระบบ",
"attention_label": "กะพริบแถบงานเมื่อมีเมลใหม่",
"attention_mac_label": "ให้ไอคอน Dock เด้งเมื่อมีเมลใหม่",
"attention_description": "เฉพาะเมื่อแอปไม่ได้อยู่ด้านหน้า",
"quiet_hours_label": "ช่วงเวลาเงียบ",
"quiet_hours_description": "ไม่มีการแจ้งเตือนและเสียงในช่วงนี้ เมลยังเข้ามาตามปกติ",
"quiet_hours_from": "จาก",
"quiet_hours_to": "ถึง",
"show_shortcut_label": "แสดงแอปด้วยแป้นพิมพ์ลัด",
"show_shortcut_description": "{keys} นำแอปขึ้นมาด้านหน้าจากแอปอื่นใดก็ได้",
"show_shortcut_taken": "แอปอื่นใช้ {keys} อยู่ ปิดแอปนั้น หรือปิดแล้วเปิดตัวเลือกนี้อีกครั้ง",
"default_app_label": "แอปเมลเริ่มต้น",
"default_app_description": "เปิดลิงก์เมลและไฟล์ .eml, .ics และ .vcf ด้วยแอปนี้",
"default_app_is_default": "แอปนี้เปิดลิงก์เมลของคุณ",
"default_app_button": "เลือกแอปเริ่มต้น…",
"new_mail": "เมลใหม่",
"no_subject": "(ไม่มีหัวเรื่อง)",
"action_delete": "ลบ",
"copy_as_file": "คัดลอกเป็นไฟล์",
"copied": "คัดลอกไฟล์แล้ว คุณวางลงในโฟลเดอร์ได้",
"copy_failed": "คัดลอกไฟล์ไม่ได้",
"reply_lost": "ไม่สามารถเก็บคำตอบที่พิมพ์ไว้ได้ เปิดข้อความแล้วตอบจากที่นั่น"
},
"desktop_sign_in": {
"browser_button": "ลงชื่อเข้าใช้ด้วยเบราว์เซอร์",
"browser_waiting": "ลงชื่อเข้าใช้ให้เสร็จในเบราว์เซอร์",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Bu ileti {folder} klasörüne taşındı.",
"window_not_ready": "Yeni pencere zamanında açılmadı. Taslağınız burada kalıyor."
},
"desktopOs": {
"section_title": "Masaüstü uygulaması",
"section_description": "Uygulamanın bilgisayarınızla nasıl çalıştığı.",
"close_to_tray_label": "Kapatınca sistem tepsisinde çalışmaya devam et",
"close_to_tray_description": "Pencereyi kapattıktan sonra da yeni postalar ve anımsatıcılar gelmeye devam eder.",
"start_at_login_label": "Bilgisayarda oturum açınca başlat",
"start_at_login_description": "Oturum açtığınızda uygulama kendiliğinden başlar.",
"start_minimized_label": "Sistem tepsisinde başlat",
"start_minimized_description": "Oturum açarken pencere açılmaz. Sistem tepsisindeki simgeyle açın.",
"attention_label": "Yeni postada görev çubuğunu yanıp söndür",
"attention_mac_label": "Yeni postada Dock simgesini zıplat",
"attention_description": "Yalnızca uygulama önde değilken.",
"quiet_hours_label": "Sessiz saatler",
"quiet_hours_description": "Bu saatlerde bildirim ve ses yok. Postalar gelmeye devam eder.",
"quiet_hours_from": "Başlangıç",
"quiet_hours_to": "bitiş",
"show_shortcut_label": "Uygulamayı klavye kısayoluyla göster",
"show_shortcut_description": "{keys} uygulamayı başka herhangi bir uygulamadan öne getirir.",
"show_shortcut_taken": "{keys} başka bir uygulama tarafından kullanılıyor. O uygulamayı kapatın ya da bunu kapatıp yeniden açın.",
"default_app_label": "Varsayılan posta uygulaması",
"default_app_description": "Posta bağlantılarını ve .eml, .ics, .vcf dosyalarını bu uygulamayla aç.",
"default_app_is_default": "Posta bağlantılarınızı bu uygulama açıyor.",
"default_app_button": "Varsayılan uygulamayı seç…",
"new_mail": "Yeni posta",
"no_subject": "(konu yok)",
"action_delete": "Sil",
"copy_as_file": "Dosya olarak kopyala",
"copied": "Dosya kopyalandı. Bir klasöre yapıştırabilirsiniz.",
"copy_failed": "Dosya kopyalanamadı.",
"reply_lost": "Yazdığınız yanıt korunamadı. İletiyi açıp oradan yanıtlayın."
},
"desktop_sign_in": {
"browser_button": "Tarayıcıyla oturum aç",
"browser_waiting": "Oturum açmayı tarayıcınızda tamamlayın",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "Цього листа переміщено в {folder}.",
"window_not_ready": "Нове вікно не відкрилося вчасно. Чернетка залишається тут."
},
"desktopOs": {
"section_title": "Настільний застосунок",
"section_description": "Як застосунок працює з вашим комп’ютером.",
"close_to_tray_label": "Під час закриття працювати далі в треї",
"close_to_tray_description": "Нова пошта й нагадування надходять і після закриття вікна.",
"start_at_login_label": "Запускати під час входу в систему",
"start_at_login_description": "Застосунок запускається сам, коли ви входите.",
"start_minimized_label": "Запускати в треї",
"start_minimized_description": "Під час входу вікно не відкривається. Відкрийте його значком у треї.",
"attention_label": "Блимати на панелі завдань за нової пошти",
"attention_mac_label": "Підстрибувати значком у Dock за нової пошти",
"attention_description": "Лише коли застосунок не на передньому плані.",
"quiet_hours_label": "Тихі години",
"quiet_hours_description": "У цей час немає сповіщень і звуків. Пошта надходить далі.",
"quiet_hours_from": "З",
"quiet_hours_to": "до",
"show_shortcut_label": "Показувати застосунок сполученням клавіш",
"show_shortcut_description": "{keys} виводить застосунок на передній план із будь-якого іншого.",
"show_shortcut_taken": "{keys} використовує інший застосунок. Закрийте його або вимкніть і знову увімкніть цей параметр.",
"default_app_label": "Поштовий застосунок за замовчуванням",
"default_app_description": "Відкривати поштові посилання та файли .eml, .ics і .vcf у цьому застосунку.",
"default_app_is_default": "Цей застосунок відкриває ваші поштові посилання.",
"default_app_button": "Вибрати застосунок за замовчуванням…",
"new_mail": "Нова пошта",
"no_subject": "(без теми)",
"action_delete": "Видалити",
"copy_as_file": "Копіювати як файл",
"copied": "Файл скопійовано. Його можна вставити в папку.",
"copy_failed": "Не вдалося скопіювати файл.",
"reply_lost": "Не вдалося зберегти введену відповідь. Відкрийте лист і дайте відповідь у ньому."
},
"desktop_sign_in": {
"browser_button": "Увійти через браузер",
"browser_waiting": "Завершіть вхід у браузері",
+31
View File
@@ -6557,6 +6557,37 @@
"message_moved_to": "Thư này đã được chuyển đến {folder}.",
"window_not_ready": "Cửa sổ mới không mở kịp. Bản nháp của bạn vẫn ở đây."
},
"desktopOs": {
"section_title": "Ứng dụng máy tính",
"section_description": "Cách ứng dụng hoạt động với máy tính của bạn.",
"close_to_tray_label": "Tiếp tục chạy ở khay hệ thống khi đóng",
"close_to_tray_description": "Thư mới và lời nhắc vẫn đến sau khi bạn đóng cửa sổ.",
"start_at_login_label": "Khởi động khi đăng nhập vào máy tính",
"start_at_login_description": "Ứng dụng tự khởi động khi bạn đăng nhập.",
"start_minimized_label": "Khởi động ở khay hệ thống",
"start_minimized_description": "Không có cửa sổ nào mở khi đăng nhập. Mở bằng biểu tượng ở khay hệ thống.",
"attention_label": "Nhấp nháy thanh tác vụ khi có thư mới",
"attention_mac_label": "Làm biểu tượng Dock nảy khi có thư mới",
"attention_description": "Chỉ khi ứng dụng không ở phía trước.",
"quiet_hours_label": "Giờ yên tĩnh",
"quiet_hours_description": "Không có thông báo hay âm thanh vào giờ này. Thư vẫn đến.",
"quiet_hours_from": "Từ",
"quiet_hours_to": "đến",
"show_shortcut_label": "Hiện ứng dụng bằng phím tắt",
"show_shortcut_description": "{keys} đưa ứng dụng lên trước từ bất kỳ ứng dụng nào khác.",
"show_shortcut_taken": "Ứng dụng khác đang dùng {keys}. Hãy đóng ứng dụng đó, hoặc tắt rồi bật lại tùy chọn này.",
"default_app_label": "Ứng dụng thư mặc định",
"default_app_description": "Mở liên kết thư và tệp .eml, .ics, .vcf bằng ứng dụng này.",
"default_app_is_default": "Ứng dụng này mở các liên kết thư của bạn.",
"default_app_button": "Chọn ứng dụng mặc định…",
"new_mail": "Thư mới",
"no_subject": "(không có chủ đề)",
"action_delete": "Xóa",
"copy_as_file": "Sao chép dưới dạng tệp",
"copied": "Đã sao chép tệp. Bạn có thể dán vào một thư mục.",
"copy_failed": "Không thể sao chép tệp.",
"reply_lost": "Không thể giữ câu trả lời bạn đã nhập. Hãy mở thư và trả lời ở đó."
},
"desktop_sign_in": {
"browser_button": "Đăng nhập bằng trình duyệt",
"browser_waiting": "Hoàn tất đăng nhập trong trình duyệt",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "這封郵件已移到 {folder}。",
"window_not_ready": "新視窗未能及時開啟。你的草稿仍保留在這裡。"
},
"desktopOs": {
"section_title": "桌面應用程式",
"section_description": "應用程式如何與您的電腦配合。",
"close_to_tray_label": "關閉時繼續在系統匣中執行",
"close_to_tray_description": "關閉視窗後,新郵件和提醒仍會送達。",
"start_at_login_label": "登入電腦時啟動",
"start_at_login_description": "您登入時應用程式會自動啟動。",
"start_minimized_label": "在系統匣中啟動",
"start_minimized_description": "登入時不開啟視窗。請透過系統匣圖示開啟。",
"attention_label": "有新郵件時閃爍工作列",
"attention_mac_label": "有新郵件時讓 Dock 圖示跳動",
"attention_description": "僅在應用程式不在前景時。",
"quiet_hours_label": "勿擾時段",
"quiet_hours_description": "此時段沒有通知和聲音。郵件仍會送達。",
"quiet_hours_from": "從",
"quiet_hours_to": "到",
"show_shortcut_label": "使用鍵盤快速鍵顯示應用程式",
"show_shortcut_description": "{keys} 可從任何其他應用程式將此應用程式帶到前景。",
"show_shortcut_taken": "其他應用程式正在使用 {keys}。請關閉該應用程式,或關閉此選項後再開啟。",
"default_app_label": "預設郵件應用程式",
"default_app_description": "使用此應用程式開啟郵件連結以及 .eml、.ics 和 .vcf 檔案。",
"default_app_is_default": "此應用程式會開啟您的郵件連結。",
"default_app_button": "選擇預設應用程式…",
"new_mail": "新郵件",
"no_subject": "(無主旨)",
"action_delete": "刪除",
"copy_as_file": "複製為檔案",
"copied": "檔案已複製。您可以將其貼到資料夾中。",
"copy_failed": "無法複製檔案。",
"reply_lost": "無法保留您輸入的回覆。請開啟郵件並在其中回覆。"
},
"desktop_sign_in": {
"browser_button": "使用瀏覽器登入",
"browser_waiting": "請在瀏覽器中完成登入",
+31
View File
@@ -6545,6 +6545,37 @@
"message_moved_to": "这封邮件已移动到 {folder}。",
"window_not_ready": "新窗口未能及时打开。你的草稿仍保留在这里。"
},
"desktopOs": {
"section_title": "桌面应用",
"section_description": "应用如何与你的电脑配合。",
"close_to_tray_label": "关闭时继续在托盘中运行",
"close_to_tray_description": "关闭窗口后,新邮件和提醒仍会到达。",
"start_at_login_label": "登录电脑时启动",
"start_at_login_description": "你登录时应用会自动启动。",
"start_minimized_label": "在托盘中启动",
"start_minimized_description": "登录时不打开窗口。请通过托盘图标打开。",
"attention_label": "有新邮件时闪烁任务栏",
"attention_mac_label": "有新邮件时让 Dock 图标跳动",
"attention_description": "仅在应用不在前台时。",
"quiet_hours_label": "免打扰时段",
"quiet_hours_description": "此时段没有通知和声音。邮件仍会到达。",
"quiet_hours_from": "从",
"quiet_hours_to": "到",
"show_shortcut_label": "使用键盘快捷键显示应用",
"show_shortcut_description": "{keys} 可从任何其他应用把此应用带到前台。",
"show_shortcut_taken": "其他应用正在使用 {keys}。请关闭该应用,或关闭此选项后重新打开。",
"default_app_label": "默认邮件应用",
"default_app_description": "用此应用打开邮件链接以及 .eml、.ics 和 .vcf 文件。",
"default_app_is_default": "此应用会打开你的邮件链接。",
"default_app_button": "选择默认应用…",
"new_mail": "新邮件",
"no_subject": "(无主题)",
"action_delete": "删除",
"copy_as_file": "复制为文件",
"copied": "文件已复制。你可以将其粘贴到文件夹中。",
"copy_failed": "无法复制文件。",
"reply_lost": "无法保留你输入的回复。请打开邮件并在其中回复。"
},
"desktop_sign_in": {
"browser_button": "使用浏览器登录",
"browser_waiting": "请在浏览器中完成登录",